بسم الله الرحمن الرحيم
أخوتي أعضاء هذا المنتدى , هذا الموضوع قام بكتابته الأخ تركي العسيري وأدراجه ضمن مواضيع موقع الموسوعة العربية , وللاهمية هذا الموضوع من وجهة نظري فقد قمت بنسخ هذا الموضوع بالكامل وأدراجه في موقعنا المميز (الفريق العربي للبرمجة) وذلك لكي تعم الفائدة لكل اعضاء هذا المنتدى .
والسلام عليكم ورحمة الله وبركاته
مقدمة
مما لا شك فيه، مئات الآلاف من البرامج التطبيقية لبست زيها الجديد وهاجرت بلا عودة من النظم التي يشكل فيها الرمز حجر الزاوية كنظام MS-DOS إلى نظم تشغيل Windows . ومما لا شك فيه أيضا، نسبة المبرمجين الذي يطورون برامجهم اليومية يفضلون تطويرها تحت نظم Windows بالذات بعد انتشار أدوات التطوير السحرية والتي تدعى بالمرئية Visual. لكن المشكلة إن معظم هؤلاء المبرمجين -لاحظ إنني قلت معظم وليس كلهم- يسمون أنفسهم بمبرمجي Windows بعدما استطاعوا بنقرات بسيطة بالفأرة إنشاء نافذة ووضع أدوات عليها ويهم بكل ثقة يلقبون أنفسهم بمبرمجي تطبيقات تحت بيئة Windows متجاهلين الكثير من الأمور التي لابد من معرفتها قبل البرمجة الفعلية. صحيح في ايام بيئة الـ DOS كان المطلوب شئ واحد للتمكن من البرمجة وهو تعلمك لغة برمجة، اما مع Windows فلابد من معرفة ذلك النظام وطريقة تعامله مع برامجك حتى تتمكن من انشاء برامج ناجحة. هذه الامور كثيرة جدا مثل مسارات التنفيذ Threading الرسائل Messgaes النظام المحمي Protect Mode الخ.. من الامور التي احببت توضيحها في الفترة القادمة والتي تعتبر من المسائل الضرورية لاي مبرمج تحت بيئة Windows مهما كانت لغة البرمجة التي يستخدمها.
فخلال الايام القادمة باذن الله، سنناقش الكثير من الامور التي تتعلق بالنظام Windows من نظرة خاصة للمبرمجين وليس للمستخدمين. فساتكلم عن طريقة توزيع النظام لذاكرة الجهاز للبرامج، وكيفية تنظيم الـ Processes وانواع الـ Threading والمقصد من الـ Multitasking والكثير من الامور التي ستجد فيها الكثير من الفائدة خاصة اذا كنت مقبل على الدخول الى امتحانات Microsoft والحصول على الشهادة.
المشكلة في مثل هذه المعلومات هي طريقة تنظيمها. لذلك، اردت توضيح الخطة التي سنستمر عليها ومعرفة رأيكم فيها قبل البدء في شرحها:
1) ماهي النوافذ ؟
2) الرسائل Messages
3) التعامل مع الصور والرسوم
4) التطبيقات العاملة Processes ومسارات التنفيذ Threading
5) ادارة الذاكرة
الاختلافات بين برامج الـ DOS وبرامج الـ Windows
في البداية اود ان اظهر بعض الاختلافات بين تصميم برامج الـ DOS وبرامج الـ Windows حتى تتضح الامور:
1) بيئة Windows مسيرة بالاحداث Event Driven، ففي البرامج التي تعمل تحت نظام التشغيل DOS انت المسؤول الاول والاخير عن عمليات مراقبة اجهزة وحدة الادخال Input Units كلوحة المفاتيح وغيرها والتي كنت تنجهزها بواسطة حلقات تكرارية او ما شابه ذلك. اما مع Windows، فالوضع يختلف تماما. فنظام التشغيل هو المسؤول عن هذه العمليات ويقوم برتجمتها وتفسيرها ومن ثم ارسالها لبرنامج فتأتيك زي البيضة المئشرة! كما سنتعرف لاحقا على الرسائل Messages.
2) نظام Windows عبارة عن نظام متعدد المهام MultiTasking وهي باختصار على انه نظام يسمح بتشغيل اكثر من تطبيق في وقت واحد. مما يحتاج الى تقنيات تسمح بادارة الذاكرة وتنظيم العمليات عليها حتى يعمل كل تطبيق باستقرار. سنرى ذلك لاحقا في ادارة الذاكرة من قبل نظام التشغيل.
3) نظام Windows موحد لمقاييس التعامل مع الاجهزة Hardware. ففي نظم DOS، كان لابد من ارفاق جميع مشغلات الاجهزة Device Drivers حتى يعمل البرنامج. فلو تذكرون عدد الملفات التي كانت تاتي مع Lotus 123 -والتي تختص فقط في تعريف مئات الطابعات- ستقدرون نظام Windows وما يفعله من اجل عدم اشغال المبرمج بنوعية الطابعة التي سيستخدمها المستخدم. فكل ما عليك هو ارسال اوامر الطباعة والباقي على النظام Windows. المزيد ايضا، كملفات الخطوط، كروت الشاشة، كروت الاصوات الخ ...
4) نظام Windows له معايير خاصة لجعل البرامج العاملة عليه متوافقة.
في الحقيقة مهما كانت اللغة التي تستخدمها لتطوير برنامج يعمل تحت Windows كـ C Java Fortran VB الخ... فان جميع هذه اللغات تقوم بعملية استدعاء لدوال واجراءت تعرف بواجهة برمجة التطبيقات Application Programming Interface -تختصر API. وهي عبارة عن الالاف الدوال تعتبر القلب النابض لتطبيقات Windows. فعندما تنشئ نافذة جديدة -بلغة VB مثلا- فكل ما تقوم به هي عملية اختيار الامر Add New Form وتصبح النافذة جاهزة. تقنيا، قام VB باستدعاء عشرات -ان لم يكن مئات- الاجراءات التابعة لـ API من اجل انشاء النافذة، لعل ابرزها دالة CreateWindow. معظم هذه الاجراءات موجودة في مئات ملفات الـ DLL والتابعة لنظام التشغيل.
اذا، نظام Windows عبارة عن اجراءات API تخدم المبرمجين لتوفير لهم كل ما يحتاجونه من اجراءات لتصميم تطبيقات متوافقة مع Windows. رغم ان اعداد ملفات الـ DLL التي تأتي مع نظام التشغيل تفوق عدد شعرات الرأس -وصف بلاغي- الا انه توجد ثلاثة ملفات هي اهم عناصر نظام التشغيل Windows وهي:
1) USER32.DLL
وهي مكتبة توفر اجراءات مسئولة عن اظهار النوافذ، القوائم، مؤشرات الفأرة ولوحة المفاتيح وغيرها...
2) KERNEL32.DLL
توفر اجراءات خاصة بادارة الذاكرة، البرامج التي تعمل بالذاكرة Processes، مصادر النظام System Resources وغيرها..
3) GDI32.DLL
وهي خاصة بالصور والرسوم التي تظهر على الشاشة، الخطوط، فرش الرسم، الالوان الخ...
هذه هي المكتبات الرئيسة لنظم التشغيل Windows. وباقي الملفات تعتبر فرعية لاداء اغراض معينة. فمكتبة COMCTL32.DLL هي خاصة بادوات Windows الشائعة Windows Common Controls والتي ظهرت منذ الاصدار Win95. ومكتبة MAPI32.DLL خاصة لعمليات البريد الالكتروني. وكتبة WinMM.DLL خاصة لاغراض الوسائط المتعددة، ومكتبة ... و.. و... ما راح نخلص يا شباب!
سأبدا الان بالتحدث عن النوافذ التي توفرها مكتبة USER32.DLL
ماهي النافذة Window ؟؟
للوهلة الاولى يتبادر اليك ان النافذة هي عبارة عن مربع يحتوي على عنوان وقائمة رئيسية مع ازرار التكبير والتصغير والاغلاق والتي تستطيع ان تحركها بكل انسيابية باستخدام الفأرة. مع ان الكلام هذا صحيح لكنه ليس دقيق التعريف. فكل شئ تراه اما عينك عبارة عن نافذة !! شريط التمرير Scorl Bar عبارة عن نافذة! وزر ابدأ Start عبارة عن نافذة! وز الاغلاق Close عبارة عن نافذة ايضا! وحتى خانة النص التي اكتب عليها مقالتي Text Box عبارة عن نافذة ايضا!! باختصار، كل شئ تستطيع رؤيته على الشاشة عبارة عن نافذة!!
والسؤال الذي يطرح نفسه الان، كيف تعتبر خانة النص Text Box واشرطة التمرير Scorl Bars وكل شئ تراه نوافذ؟؟ وكيف يستطيع نظام Windows التمييز بينها؟ والجواب هو عن طريق فئة النافذة Window Class, التحدث عن فئة النافذة يتطلب مقالة كاملة لشرحها، لكن -بشكل مؤقت- اعرف ان فئة النافذة عبارة عن قيم تحدد نوع النافذة او الاداة هل هي نافذة حقيقة ام زر اوامر ام خانة نص الخ...
يوجد فئات قياسية وهي تمثل ادوات Windows القياسية كزر الاوامر، خانة النص، اشرطة التمرير الخ.. كما توجد مجموعة ادوات تعرف بادوات Windows الشائعة والتي ظهرت مع الاصدار Win95 كما قلنا.
اهم نقطة اريد توضيحها هنا تجدها بعد الفاصلة، اي نافذة موجودة على الشاشة او مخفية -لكن حية- يوجد لها رقم يعرفها ويميزها عن غيرها، يعرف هذا الرقم بمقبض النافذة Window Handle يختصر بـ hWnd. فاذا سيرتك الايام واحتجت لاستخدام احد اجراءات API والتي تتطلب مقبض النافذة hWnd فهي تطلب رقم من نوع Long قد ارسله نظام التشغيل الى تلك الاداة او النافذة ليميزها عن غيرها. هذا الرقم يعطيه نظام التشغيل عندما يقوم بانشاء الاداة، وتضل الاداة محتفظة بهذا الرقم حتى تلغى من الذاكرة. لن تستطيع ان تحدد مقبض الاداة بنفسك فهي مهمة نظام التشغيل، كذلك لن تستطيع تغيير هذا الرقم. الخلاصة التي اود ان اقولها هي: ان نظام التشغيل يتعرف على جميع النوافذ والادوات عن طريق هذا الرقم. فكل اجراءت API والخاصة بالنوافذة والادوات ستطلب منك هذا الرقم.
اجراءات API و مقدمة عن الرسائل
عودة الى موضوع اجراءات API، من المهم ان تضع في ذهنك اصدار Windows المناسب الذي تستدعي اجراءات API الخاصة به. فهنالك العديد من اجراءات API غير متوافقة مع الاصدارات المختلفة لـ Windows! احد هذه الاجراءات قد واجهته قبل عدة ايام وهو GetDiskFreeSpace والخاص بمعرفة المساحة المتوفرة في القرص المحدد. فعندما قمت بتجربته تحت Windows 2000 عمل بنجاح، لكن عندما نبهني احد الاخوان في رسالة تقتضي ان الاجراء لم يعمل بشكل صحيح تحت نظام Windows ME راجعت مكتبة MSDN وتبين لي ان هذا الاجراء متوافق مع نظم Windows NT و Windows 2000 وبعد مساعدة كريمة منه نبهني الى وجود الاجراء GetDiskFreeSpaceEx والذي يتوافق مع جميع اصدارات Windows المختلفة والذي عمل بنجاح معي. في معظم الاحوال، الاجراءات التي تنتهي بالحروف Ex تكون متوافقة مع جميع اصدارات Windows فهي اجراءت مطورة او محدثة من اجراءات سابقة لها.
تجرني القصة السابق الى الحديث عن موضوع توافقية اجراءت API لاصدارات Windows المختلفة وهي احدى المشاكل الخطيرة جدا التي تصادف مطوروا برامج Windows والذين يفرض عليهم تزامن التوافقية لبرامجهم حتى تعمل تحت اصدارات Windows المختلفة.
والحل الذي يطبق في معظم البرامج الجدية يختلف من طريقة تسويقية الى اخرى. فمثلا، يقوم بعض المبرمجين بانشاء اصدار من البرنامج خاص لـ Windows 9x واصدار اخر خاص لـ Window NT قد تكون طريقة مجدية الا انني اعتقد انها مكلفة. فمن المعروف ان كتابة الف سطر لبرنامج من الصفر خير من عملية تنقيح برنامج مكون من مائة سطر! وهي القاعدة التي يستند عليها مهندسوا البرامج Software Engineer. ويبدو ان الطريقة الافضل هي استخدام جمل الشرط عند كل عملية استدعاء اجراء من اجراءات API فقد تكتب شيئا مثل:
===============
If Windows9x Then Call Win9xAPI
Else If WindowNT Then Call WinNTAPI
===============
الذي اقصده من الخوارزم السابق، هي عملية اختبار اصدار Windows الحالي ومن ثم استدعاء اجراء API المناسب او المتوافق معه، وبهذه الطريقة ستضمن استقرار عملية تنفيذ برنامجك. فلا تغتر كثيرا اذا شاهدت برنامجك عمل بنجاح تحت اصدارات Windows المختلفة! لانك عاجلا او اجلا ستستخدم اجراءات API والتي تتطلب الحذر الشديد عندما تلعب بها، فلا تلعب بالنار - تحرء صبيعك! والي بيشتري برامجك، يرجع يبيعها لك!!!
اخيرا، اعود الى موضوع مقبض النافذة، مقبض النافذة عبارة عن قيمة عددية صحيحة حجمها 16 bit في نظم Windows 3.x و 32 bit في نظم Windows 9x و NT و 2000. اتوقع ان حجمها سيكون 64 bit في نظم Windows XP والعاملة تحت مسارات الـ 64 bit. اردت التنويه هنا حول حجمها كي تعين قيمها في انواع مناسبة للمتغيرات. فكما يعمل مبرمجوا لغة C، ان المتغيرات Int -ليس Long Int- حجمها 2 بايت، واذا قمت باسناد قيمة مقبض حجمه long int وكان حجم القيمة اكبر من int، فان قيمة المتغير ستعود الى الصفر وتزيد بمقدار الفارق مما يؤثر بشكل سلبي ليس فقط على برنامج وانما يشمل النوافذ المفتوحة في البرامج الاخرى! لا تنسى يا عسل انك قد ترسل قيمة مقبض لاداة او نافذة اخرى!
حسنا والان لنغير الموضوع قليلا ونتكلم عن موضوع مهم جدا جدا وهو الرسائل Messages...
* الرسائل - فكرة عامة
عندما يقوم شخص اسمه "عباس السريع" مثلا بفتح بريده الالكتروني في وقت من اوقات حياته، سيقرأ رسالة تخبره "لقد توفى ابوك" او "لقد فزت بالمليون". بغض النظر عن المحتوى التشاؤمي او التفاؤلي للرسالة، فهي رسالة هدفها اخباره بوقوع (((حدث))) معين. كلمة (((حدث))) حط عليها 9848923742 خط! فهي مهمة جدا جدا! اذن فالحدث Event عبارة عن رسالة تمت من مرسل ومرسل له. وهذا بالضبط الذي يحدث مع عالم برمجة Windows!!!!!!
فعندما يقوم المستخدم بالنقر باصابع يده الناعمتين على نافذة، يقوم فورا نظام التشغيل بارسال رسالة الى تلك النافذة يخبرها "بان المستخدم قد قام بالنقر عليها" وستقوم النافذة باتخاذ الاجراء اللازم للتفاعل مع تلك الرسالة.
السيناريو السابق هو ملخص مقالتي الان، فعملية وقوع الاحداث Events التي تقع على النوافذ والادوات والتي قد تكون نقرات بالفأرة او لوحة المفاتيح الخ... هي عبارة عن رسائل مرسلة من نظام التشغيل الى برنامجك.
تقنيا، يوجد نوعان من الرسائل تحت بيئة Windows. النوع الاول رسال التنبيه Notification Messages وهي عبارة عن رسائل من نظام التشغيل الى برنامجك. والنوع الثاني هي رسائل التحكم Control Message وهي عبارة عن رسائل من برنامج الى برامج اخرى!. نعم نعم فلا تعتقد ان برنامجك يستقبل رسائل -مثل عامل السنترال- فقط، بل يمكن ان يرسل رسائل -كمدراء الاقسام- الى جميع البرامج حاله كحال النظام -فكلنا سواسيه- والطريقة التي اعرفها هي باستخدام الاجراء SendMessage وهو احد اجراءات API المشهورة.
لكن يا شباب يبدو انني نسيت توضيح ماهي حقيقة الرسالة؟ هل هي رسالة صوتية مرئية ام ماذا؟؟؟ والحقيقة انها عبارة عن قيمة لا راحت ولا جت حالها كحال مقبض النافذة! فهي قيمة عددية صحيحة من نوع 16bit او 32bit على حسب الاصدار. لكن وبسبب وجود الالاف الرسائل، تجد معظم لغات البرمجة توفر قيم الرسائل للمبرمج على شكل ثوابت لها بادئة WM كـ WM_CLICK او WM_MOUSEMOVE الخ..
* برنامجي كيف يتعامل مع الرسائل؟
في مثالي السابق -الخاص بعباس السريع- يتعامل مع الرسائل بطريقة تسجيل الدخول الى بريده ومن ثم قراءتها واخيرا التفاعل مع نوع الرسائل فقد يحزن او يفرح. كذلك الحال مع برنامجك، فانت -يا مبرمج- صاحب القرار الاول والاخير لتحديد طريقة التفاعل مع الرسالة وذلك بكتابة الكود المناسب في المكان المناسب/ والذي يختلف انجازه من لغة برمجة الى اخرى. مع ذلك، تتشارك جميع لغات البرمجة في خوارزم لاستقبال الرسائل والذي سأوضح في السطور التالية.
عندما تقوم بفتح نافذة فانك -انت او لغة البرمجة- قد احدثت حلقة تكرارية لا نهائية قائلة:
كلمة ولو جبر خاطر ولا سلام من بعيد
................. ولا رسالة يا هاجر بيد ساعي البريد
الكملة التي ستجبر خاطر النافذة هي الرسالة التي تريدها، وساعي البريد هو نظام التشغيل. تصوروا ان عملية انتظار الرسالة تتم في حلقة تكرارية وبالتأكيد فكر ماذا سيحدث لو وجدت 100 نافذة مفتوحة في وقت واحد وكل واحدة منها لها حلقة تكرارية طالبة رسالة من نظام التشغيل!!! فلو كان الجهاز يحتوي على ذاكرة عالية ومعالج سريع لتنفيذ جميع حلقات التكرار سيوفر نظام التشغيل جميع الرسائل الى اصحابها. اما في حالة كون الجهاز موب لهناك! فهذه النوافذ ستسبب التقليل من مصادر النظام مما يؤدي الى انهيار نظام التشغيل قائلا: احنا نصرف على مين ولا على مين!!! وستظر الشاشة الزرقاء المحبوبة "System Busy" . لا تخف تراني امزح، فهذا شئ نادر الحدوث بسبب كثرة النوافذ المفتوحة لكن المقصد من ذلك هو تبين ان المعالج يقوم بتنفيذ عشرات الالاف -ان لم يكن ملايين- من التعليمات في حلقات تكرارية للنوافذ والتي غرضها فقط انتظار الرسائل من نظام التشغيل!! هذا غير تنفيذ الاكواد التي كتبتها!
لتوضيح الفكرة اكثر، دعنا نفترض انك انشئت نافذة وتريد ان تجعلها جاهزة لاستقبال الرسائلة. فالخوارزم الذي ستتبعه سيكون مشابه لهذا:
===============
Do
{
GetMessage ( &lMsg, &lPara1, &lPara2);
switch ( lMsg )
{
case: WM_CLICK
OnWindowClick ();
case: WM_MOUSEMOVE
OnWindowMouseMove ( lPara1, lPara2);
.
.
.
}
} While ( lMsg = WM_CLOSEWINDOW )
==============
اذن، في الخوارزم السابق انشئنا حلقة تكرارية هدفها قنص رسائل النظام والموجهة الى النافذة عن طريق الاجراء GetMessage، وستنتهي الحلق اذا كانت الرسالة WM_CLOSEWINDOW والتي بكل تأكيد توضح اننا لن نحتاج الى عملية قنص الرسائل ما دام المستخدم طلب اغلاق النافذة. استخدم عبارة الشرط switch ... case لتحديد نوع الراسلة ومن ثم تنفيذ الاجراء المناسب.
اذا كان الكود السابق غريب عليك، فيبدو انك من المبرمجين حديثوا العهد بـ Windows والمعاصرين للبرمجة المرئية Visual، لكن الشئ الذي لابد من معرفته هو انه مهما كانت لغة البرمجة التي تستخدمها، فهذا الخوارزم هو الخوارزم الذي تتبعه لغة البرمجة وتشئ الاكواد المناسب له والذي يعرف في عالم لفات البرمجة المرئية بالاحداث Events والتي هي عبارة عن اجراءات قد رتبتها لك لغة البرمجة ويتم استدعاءها وفقا لنوعية الرسائل التي وصلت للنافذة من نظام التشغيل. مع ذلك، عندما تحدك الايام ستضطر الى الاكواد مماثلة للخوارزم السابق وستطبق تقنيات متقدمة في برمجة رسائل النظام Windows كالردود CallBack او التصنيف الفرعي للرسائل Subclassing Windows Messages.
الصور والرسوم في نظام Windows
من المفيد ان اذكر انها، ان عملية ارسال الرسائل الى النوافذ تتم بشكل منظم في طابور يعرف باسم طابور الرسائل Message Queue. طريقة التعامل مع هذا الطابور تتبع اسلوب FCFS اقصد ان الاول يخدم الاول First Come First Served. مذا يعني هذا الكلام؟ قد يفهم مغزاه بتوضيح مثال. فاحيانا في حالة كون نظام التشغيل مشغول جدا تقوم باجراء عدة عمليات بالفأرة كنقرات او سحب الخ.. يقوم نظام التشغيل بوضع جميع هذه العمليات في طابور الرسائل، وعندما ينتهي نظام التشغيل من المهمة التي اشغلته، تلاحظ عمليات استجابة الرسائل حدثت بشكل مرتب. من او رسالة الى اخر رسالة. جرب مثلا القيام بعملية تشغيل برنامج ثقيل -حتى تجعل النظام مشغول في تحميله- في هذه الاثناء قم بعمل قد ما تستطيع من احداث بالفأرة، ستلاحظ بعد انتهاء عملية تحميل البرنامج الثقيل، ان جميع الاحداث التي سببتها الفأرة قد تم تنفيذها. وهذا الذي لابد من وضعه في الذهن دائما خاصة اذا كانت برنامج يتأثر باحداث الفأرة بشكل ملحوظ او حتى غيرها من احداث.
اختم موضوع الرسائل بالتوضيح البرمجي لسبب عدم استجابة الاحداث. في احيان قليلة جدا تلاحظ انك عندما تقوم بالنقر على النافذة او اي زر لا يتم اي شئ! ولكن سرعان ما تعيد عملية النقر لتتم الامور بشكلها الطبيعي. السبب في الحقيقة ليس فيزيائي في معمارية او تركيبة جهاز او آلة الفأرة -لانها قد ارسلت الرسالة، لكن السبب هو برمجي. وواقعيا، نسبة حدوث هذا الامر ضئيلة جدا قد تكون مقاربة لل، 001. % لكني احببت توضيح السبب، فلا تنسى انت مبرمج ولست مستخدم!
اذا رجعت
الى الخوارزم الموجود في ردي السابق والخاص باستقبال وتفسير الرسائل ستعرف السبب. فقد يرسل نظام التشغيل الى النافذة رسالة لكن عملية استجابة الرسالة في الحلقة التكرارية قد تعدت الشرط Case الخاص بتنفيذ الرسالة. اعلم ان كلامي معقد قليلا، لكن ركز في المثال السابق وافترض هذا السيناريو:
بما ان الكود موجود في حلقة تكرارية فان عملية تنفيذ الاوامر الموجودة في الحلقة التكرارية ستكون لا نهائية، حسنا افترض ان عملية التنفيذ قد وصلت الى الشرط case WM_MOUSEMOVE وهي اللحظة التي قمت فيها بالنقر على النافذة -اي ارسال الرسالة WM_CLICK- فان المتغير lMsg لا يحتوي على قيمة تمثل الرسالة WM_CLICK وستستمر عملية تنفيذ الحلقة التكرارية بشكل طبيعي دون التأثر بالرسالة التي قد ارسلها نظام التشغيل لكن برنامجك لم يتمكن من قنصها.
صحيح ان السيناريو السابق نادر الحدوث، الا انني احببت -كما قلت- توضح الفكرة. قد تزداد نسبة حدوث هذه المشكلة بزيادة عدد الجمل الشرطية case، لكن لا تخف، فالزيادة نسبية ولن تؤثر بشكل حسي.
حان الوقت الان للتحدث عن كيفية تعامل نظام التشغيل Windows مع الصور والرسوم:
بيئة Windows هي بيئة من النوع واجهة المستخدم الرسومية GUI فالنقطة Pixel تمثل حجر الاساس لكل شئ يعرضه لك نظام التشغيل، مما يستلزم تنظيم عمليات عرض الصور والرسوم. التحدث عن طرق ادارة الصور والرسوم موضوع يحتاج -دون مبالغة- كتاب كامل! لكن ساحاول بقدر المستطاع توضيح النقاط الاساسية بشكل مختصر هنا. وسأتحدث عن مبدأ اعادة الرسم وسياقات الاجهزة.
* مبدأ اعادة الرسم Re-Draw
المظاهر خداعة، والذي يدعي ان انه لا يغتر بالمظاهر فهو في الحقيقة اكثر انسان تخدعه المظاهر! ولعل اكثر شئ قد خدعنا نظام التشغيل Windows هو في تكوين او فلسفة الطبقات المتعددة للشاشة. فلو تضع نافذة س فوق النافذة ص ستكون النافذة س هي الظاهرة امامك والنافذة ص خلف النافذة س مما يوحى لنا وجود اكثر من طبقة Layer للشاشة. وبما اننا مبرمجين فلابد ان نكون متيقنين دائما ان الشاشة لا توجد فيها الا طبقة واحدة! بغض النظر عن الايحاء الذي يوهمنا به نظام التشغيل.
فالسر وراء عملية الايحاء هي عملية تعرف في عالم البرمجة المرئية باعادة الرسم ReDraw. فاذا قمت بالضغط على النافذة ص -الموجودة خلف النافذة س- فكل ما سيحدث هي عملية اعادة رسم محتويات النافذة ص مما يؤدي الى عملية الايحاء -التي ركزت عليها- ويبدو لنا ان النافذة ص قد انتقلت الى الطبقة العليا او القريبة من عين المستخدم على الشاشة. الزبدة يا اخوان هي ان كل ما تراه اما عينك عبارة عن طبقة واحدة وليست مجموعة طبقات، والسر وراء هذه الطبخة هي عملية اعادة الرسم. فلو كنت من مبرمجي C فستعلم ان الرسالة WM_PAINT تصل الى النافذة في كل مرة تحتاج الى عملية اعادة رسمها وتكتب الكود اللازم، تماما مثل الحدث Form_Paint الموجود في نافذة النموذج لمبرمجي Visual Basic.
* سياقات الاجهزة Device Context
في بيئة MS_DOS كانت عمليات الرسم تتم بطريقة غير منظمة. فتستطيع ان ترسم اي شئ في اي مكان من الشاشة، مما قد يؤثر على رسوم موجودة سابقا قد وضعتها. لذلك، انت المسؤول الاول والاخير عن عمليات اعادة رسم وحفظ المعلومات الخاصة بالرسوم السابقة لتضع فوقها رسوم جديدة. لكن مع Windows، فالوضع اكثر تنظيم وذلك بسبب سياقات الاجهزة Device Context. سياق الجهاز عبارة عن كائن من كائنات نظام التشغيل يمثل صورة او مكان للرسم، يحتوي هذا السياق على جميع المعلومات الخاصة بالصورة او الرسمة: حجم الصورة، عمق اللالوان، بيانات الصورة الخ.. وبما انه توجد عشرات من الصور التي تشاهدها من حين لاخر، فتعمد مطوروا Windows الى اعطاء امكانية للمبرمجين في انشاء عدة سياقات اجهزة. قد تكون بعضها لجهاز الطابع او لجاهز الشاشة.
والسؤال الذكي يكون كيف يستطيع نظام التشغيل التمييز بين عشرات السياقات الموجودة؟ والجواب الاذكى هو عن طريق رقم او مقبض السياق hDC ففي كل مرة تقوم بعملية انشاء سياق رسم جديد -باستخدام CreatDC مثلا- يقوم نظام التشغيل باعطاء رقم لهذا السياق او مقبض handle لهذا السياق وهي نفس فكرة مقابض النوافذ hWnd.
اذا فمقبض النافذة hWnd خاص لتمييز النوافذ، اما مقبض سياق الجهاز hDC خاص لتمييز الصور الموجودة. تذكر ان كل صورة -حتى لو كان حجمها نقطة واحدة!- لها مقبض سياق والذي يمثل بياناتها. فلو تقم الان بقطع عملية قراءة هذه المقالة للحظات وتصغير النافذة والقاء نظرة الى الايقونات الموجودة في سطح المكتب، ستعرف كم مقبض سياق موجود الان، كذلك الحال مع شعار Windows الموجود في زر ابدأ Start فهو يحتوي على سياق، ايضا الايقونات الموجودة في صينية النظام Sys Tray فكل رمز منها يحتوي على سياق، وحتى الرموز الموجودة في اعلى المتصفح (شريط الادوات) فكل صورة او رمز له سياق . المزيد ايضا، حتى الاماكن الخاصة لعرض حروف النصوص Text لها سياق! ما اقول الا: الله يكون في عون الذاكرة!!
اعيد توضيح فكرة مقبض السياق من جديد، تذكر ان مقبض السياق عبارة عن رقم يمثل سياق، والسياق يحتوي على جميع المعلومات الخاصة لعرض الصورة، الرسمة او النص. اذن، في حالة تعاملك مع اجراءات API والخاصة بالرسم، ستطلب منك رقم يمثل مقبض السياق، فلا تحاول ان ترسل لها مقبض النافذة hWND وانما مقبض سياق الجهاز hDC. احب ان انوه هنا لمبرمجي Visual Basic انه بامكانهم الحصول على مقبض سياق الجهاز والخاص بمنطقة الرسم الموجودة على النافذة عن طريق الخاصية hDC التابعة لنافذة النموذج Form Window او خانة النص PictureBox فتستطيعون كتابة شيئا مثل:
Print Form1.hDC
Print Picture1.hDC
سياق الجهاز يمكن ان يكون على الورقة المطبوعة ايضا، فتستطيع طباعة سياق موجود على النافذة الى ورقة باجراء واحد من اجراءات API -طبعا لابد من ارسال قيمة مقبض السياق. المزيد اياض، تستطيع انشاء سياقات جديدة على الشاشة، وازيدك من الشعر بيت، حتى النافذة الواحدة قد تحتوي على اكثر من سياق! لكن تذكر الحمل الذي سيكون على الذاكرة. لذلك، لابد من حذف السياق وتحرير المساحة في الذاكرة الخاصة به عن طريق الاجراء DeleteDC.
تستطيع ان تفعل اشياء كثيرة على السياقات، كالرسم فوقها، تغيير الوانها، خصائصها الخ... فلو استطع الحصول على رقم مقبض سياق سطح المكتب، فانت قادر على الرسم فوقه دون اي مشاكل. لكن تذكر انه من غير اللائق الكتابة على سياقات لا تخص برنامجك، فهي نوع من التدخل في خصوصيات المستخدم خاصة ان لم تكن لها حاجة ماسة.
) التطبيقات العاملة Processes ومسارات التنفيذ
) التطبيقات العاملة Processes ومسارات التنفيذ Threading
* تعريف البروسس
عندما تقوم بتشغيل او تنفيذ برنامج، فان البرنامج سيكون بالذاكرة طيلة فترة عمله. في هذه الاثناء، نطلق عليه الاسم برنامج عامل Process. باختصار، أي برنامج يتم تنفيذه بالذاكرة يسمى بروسس Process. واذا كنت من مستخدمي Win 2000 او NT فتستطيع معرفة جميع البرامج العاملة Processes عن طريق خانة التبويب Processes الموجودة في النافذة Windows Task Manager والتي تستطيع فتحها بالضغط على المفاتيح Ctrl + Shift + Esc
* مسارات التنفيذ
اما مسارات التنفيذ Threading فهو موضوع متقدم قليلا واود ان اعطيك فكرة مبسطة عنه الان. كل Process يحتوي على ثريد Thread واحد (على الاقل). لان البروسس قد يحتوي على اكثر من ثريد Thread ولكن الثريد الواحد لا يكون تابع الا لبروسس واحد فقط.
العادة جرت عند الكتاب بتوضيح معنى المصطلح قبل التوغل في تفاصيله ولا اعلم لماذا نسيت تعريف الثريد قبل التحدث عنه! على العموم، الثريد Thread عبارة عن مسار تنفيذي. فاي عملية تنفيذية يقوم الـ Process بانجازها تتم في ثريد Thread. مثلا، لو كان البروسس يقوم باجراء عملية حسابية فان عملية التنفيذ تتم في مسار خاص لها. تسمى عملية التنفيذ هذه بالثريد.
ذكرت قبل قليل ان الـ Process الواحد قد يحتوي على اكثر من ثريد، أي ان البروسس الواحد يقوم بعملية تنفيذ اكثر من اجراء في وقت واحد. مثلا، قد يقوم برنامج معين بالقيام بعملية طباعة المستندات، والتدقيق الاملائي لها، واجراء عملية الحفظ في وقت واحد. لاحظ انني ذكرت ثلاثة عمليات 1) طباعة 2) تدقيق 3) تخزين . يعني ان هذا البرنامج يحتوي على ثلاثة مسارات تنفيذية (ثريدز) Threads هي: ثريد خاص لعملية الطباعة وثريد خاص للتدقيق الاملائي وثريد خاص للتخزين. بما ان هذه العمليات الثلاث تتم في وقت واحد، لذلك نطلق عليها 3 ثريدات تابعة لبروسس (برنامج) واحد. اما لو كانت هذه العمليات الثلاث لا تتم في وقت واحد (أي الطباعة ثم التدقيق ثم الحفظ) فان جميع هذه العمليات الثلاث تتم في ثريد واحد. اتمنى من صميم قلبي ان تكون فكرة الثريد قد اتضحت لك، لانني سأعود اليها لاحقا.
* نظرات متقدمة الى مبدأ الـ Multitasking
يطلق على نظام Windows بنظام متعدد المهام Multitasking. مبدأ تعدد المهام Multitasking بسيط وسهل الاستيعاب، وهو امكانية تشغيل اكثر من تطبيق في وقت واحد. فمن الواضح لمبرمجي –وحتى مستخدمي- Windows قابلية هذا النظام لتشغيل اكثر من برنامج او تطبيق في وقت واحد. وبما ان هذه المقالة موجهة للمبرمجين فسأوضح برمجيا كيف يستطيع نظام التشغيل Windows بعملية تشغيل اكثر من تطبيق في واحد رغم وجود ذاكرة Memory واحدة ومعالج Processor واحد (باستثناء المعالجات المتعددة لبعض الاجهزة).
حسنا، بعد تعريف مبدأ تعدد المهام ما رأيك لو اخبرتك ان نظام التشغيل Windows لا يستطيع تشغيل اكثر من تطبيق في وقت واحد!! أي ان مبدأ تعدد المهام MultiTasking عبارة عن شئ غير حقيقي! والذي يعمل دائما تطبيق واحد فقط! كيف ستكون ردت فعلك يا ترى؟؟
يبدو ان كلامي متعارض وقد يجعلك تسهر في التفكير اكثر من العاشقين! لذلك، سنعود الى الوراء وعدد ليس ببعيد من السنين الى ايام نظم 16 bit كاصدارات Windows 3.x حتى تفهم كيف تتم عملية تنفيذ اكثر من بروسس في وقت واحد.
* نظم 16 bit
في الاصدارات القديمة لنظم Windows كان بامكانك تشغيل اكثر من تطبيق في وقت واحد، لكن عملية التنفيذ تكون موجهة لبرنامج واحد فقط وهو البرنامج النشط Active Process. فلو كان لديك برنامجان مفتوحان على سطح المكتب، فالبرنامج النشط هو الذي يعمل حاليا، اما الاخر فهو متوقف تماما ولا يقوم باي شئ، فكل ما تراه عبارة عن واجهة ثابتة تمثل الواجهة الاخيرة التي كان عليها البرنامج قبل ان يفقد التركيز او التنشيط.
فعندما تقوم بتنشيط البرنامج الثاني، يقوم نظام التشغيل باجراء جميع العمليات الازمة لاعادة انعاش واستمرار عملية تنفيذ البرنامج من النقطة التي وقف عندها. طبعا معظم هذه العمليات منخفضة المستوى low level وهي خاصة بنقل المسجلات Registers وساعة التنفيذ CPU Time واشياء خاصة بالمعالج وهي خارج عن موضوعنا الان.
اذن حتى لو وجد عشرات التطبيقات المفتوحة امامك، فتذكر ان واحد منها يعمل فقط وهو التطبيق النشط. وكل ما تراه عبارة عن ديكور لا يعمل الا اذا اصبح التطبيق هو التطبيق النشط. تذكر دائما ان المظاهر خداعة، وقد خدعنا نظام التشغيل Windows منذ زمن بعيد بانه يستطيع تنفيذ اكثر من تطبيق في وقت واحد!
والان سأثبت لك هذه الحقيقة بهذا المثال البسيط، قم بانشاء برنامج بسيط لطباعة ارقام في حلقة تكرارية كهذا البرنامج بلغة C او ما يشابهه باي لغة برمجة اخرى:
==================================
#include
void main ()
{
int iCounter = 0;
while (true)
printf ("r%d", iCounter++);
}
==================================
والان اريد منك تشغيل نسختين من هذا البرنامج، ووضع النافذتين متجانبتين على الشاشة. تلاحظ ان نسخة واحدة من النسختين يتم تنفيذها (النسخة النشطة) اما الاخرى فهي ساكنة وواقفة لا تتحرك. ولو تجعل النسخة الثانية هي النشطة ستتم عملية الايقاف المؤقت للاولى وستستمر الثانية بالعمل. وهذه طريقة تنفيذ اكثر من تطبيق –على قولتهم في وقت واحد!.
* نظم 32 bit
في نظم 32 بت والتي بدأت منذ اصدار Win 95 وما بعده اصبح عملية تنفيذ اكثر من تطبيق اكثر واقعية من نظم 16 بت. وقد تطورت ميكانيتها تطور كبير. فلو تقوم بعملية تشغيل نسختين من البرنامج السابق في وقت واحد، لن يكون هنالك وقف مؤقت! بل ستلاحظ ان كلا البرنامجين يعملان بشكل طبيعي. مع ذلك اريد ان اذكرك ان المظاهر خداعة! فالبرنامجان لا يعملان في وقت واحد، وانما تتم عملية التناوب بينهما بشكل استمراري.
لتوضيح فكرة عملية التناوب، راقب في الخطوات التالية كيف يقوم نظام التشغيل بتنفيذ تعليمات كلا البرنامجين Processes:
1) سيتحول نظام التشغيل الى النسخة الاولى ويبدأ بطباعة قيمة العداد iCounter وهي صفر
2) سينتقل نظام التشغيل الى النسخة الثانية ويبدأ بطباعة قيمة العداد iCounter وهي صفر
3) يعود نظام التشغيل الى النسخة الاولى ويطبع قيمة العداد iCounter وهي واحد
4) يعود نظام التشغيل الى النسخة الثانية ويطبع قيمة العداد iCounter وهي واحد
5) يعود نظام التشغيل مجددا الى النسخة الاولى ويطبع قيمة العداد iCounter وهي 2
وهكذا ... حتى تتم عملية انتهاء البرنامجين.
* نظم NT وفلسفة الأسبقية Priority
يبدوا ان نظم 32 بت جعلت مبدأ تعدد المهام اكثر واقعية من ذي قبل. لكن يعيب الطريقة السابقة انها لو وجدت عشر برامج Process تعمل في نفس الوقت ، فسيضطر نظام التشغيل الى تنفيذ تعليمة من كل بروسس مما يسبب بطئ المعالجة كلما وجدت بروسس جديد، ذكرت انها عيب لانك في احيان كثيرة تود ان يقوم نظام التشغيل بالتركيز على البرنامج النشط الحالي الذي تعمل به اكثر من البرامج الاخرى لزيادة سرعة المعالجة.
والحل الذي طوروه مصمموا نواة التشغيل Windows يعرف بالاسبقية Priority. وهي ببساطة شديدة اعطاء البرنامج النشط وقت اكثر وفرصة اكثر للمعالجة من البرامج الاخرى.
ساوضح مدى تأثير الاسبقية على المثال السابق، الخطوات التالية تعرض لك كيف تقوم نظم NT و 2000 بتنفيذ البرنامجين:
اذا كان النسخة الاولى هي النشطة:
1) سيتحول نظام التشغيل الى النسخة الاولى ويبدأ بطباعة قيمة العداد iCounter وهي صفر
2) سيزيد من قيمة العداد ويطبع قيمة iCounter وهي واحد
3) سيزيد من قيمة العداد ويطبع قيمة iCounter وهي 2
4) سيتحول نظام التشغيل الى النسخة الثانية ويطبع قيمة iCounter وهي صفر
5) يعود نظام التشغيل الى النسخة الاولى ويطبع قيمة iCounter وهي 3
6) سيزيد من قيمة العداد ويطبع قيمة iCounter وهي 4
7) سيزيد من قيمة العداد ويطبع قيمة iCounter وهي 5
8) سيتحول نظام التشغيل الى النسخة الثانية ويطبع قيمة iCounter وهي 1
9) يعود نظام التشغيل الى النسخة الاولى ويطبع قيمة iCounter وهي 6
وهكذا..
تلاحظ ان نظام التشغيل يقوم بتنفيذ تعليمات اكثر للنسخة الاولى للبرنامج من النسخة الثانية والسبب ان النسخة الاولى هي النشطة، لذلك يعطيها نظام التشغيل اسبقية اعلى ووقت معالجة اكثر من النسخة الثانية.
وكي اثبت لك كلامي، قم بتشغيل النسختين، وضعهما متجانبتين. ستلاحظ ان كلا النسختين تعلمان وتطبعان الارقام بسرعة. لكن لو كنت قوي الملاحظة، سنلاحظ ان النسخة النشطة هي اسرع في التنفيذ من النسخة غير النشطة.
وهذه هي فلسفة الاسبقية التي ظهرت منذ اصدار NT وحتى اصدارات 2000. تستطيع التحكم بالاسبقية عن طريق الضغظ بالزر الفأرة الايمن على البروسس في خانة التبويب Processes في النافذة Windows Task Manager .
ايضا، تستطيع زيادة الاسبقية لبرنامجك اذا اردت عن طريق اجراءات متقدمة من اجراءات API والتي بصراحة شديدة لا اود اللعب معها كثيرا، في تتطلب مبرمجين شجعان! وحتى لو كنت من المستخدمين، تستطيع الغاء تقنية الاسبقية التي يوفرها نظام Win 2000 وتجعل عملية تنفيذ البرامج كما تعمل في اصدارات Win 9x وذلك عن طريق الخطوات التالية:
1) من ايقونة My Computer اضغط الزر الايمن ثم اختر Properties
2) حدد خانة التبويب Advanced ثم اضغط على الزر Performance Options
3) حدد BackGround Services ذا اردت الغاء تطبيق مبدأ الاسبقية. او Applications اذا رغبت غير ذلك.
اخيرا، اتمنى ان يكون التوضيح غير معقد! فان مثل هذه المسائل المتقدمة قد قضى المبرمج شهور طويلة –ان لم تكن اعوام- لتعلمها واستعيباها فارجوا ان تضع ذلك في ذهنك ومعرفة مدى الصعوبة التي اواجهها في توصيل مثل هذه المواضيع لك في مقالات او دروس مبسطة!
بالتوفيق،
تركي العسيري
عن موقع الموسوعة العربية

