السلام عليكم,,
خطرت لي فكرة برنامج بسيط كتطبيق لدروس أحاول إكمالها حالياً. الدروس هي كتاب Charles Petzold الشهير في برمجة Windows و شرح فلسفتها بطريقة جميلة جداً و مفصلة كثيراً, بالطبع هدفه هو شرح المفاهيم بدلاً من شرح الدوال أو شرح مكتبات Windows بشكل أصح.
أعرف أنني لم أشارك منذ مدة, و لكن ذلك يرجع لعدة ظروف, و لكن سنعود معكم من جديد بإذن الله.
سأقوم بوضع كتاب برمجة Windows في المكتبة لمن يريد الاستفادة منه. و لكن لابد من القول بإن برمجة Windows هي علم بحد ذاته و لم أكن أعتقد أني سأستمتع بتعلم شيء جديد كهذا الشيء.
البرنامج عبارة عن محرر HEX للملفات, و بالطبع ستكون الواجهة بسيطة لأنه مشروع أفكر بتطبيقه بعد الانتهاء من قراءة عدد معين من فصول الكتاب, و لكن تركيزي في هذه المرحلة سيكون على محرك البرنامج نفسه. حتى أقوم ببناء الواجهة لاحقاً بعد كتابة و اختبار محرك البرنامج.
كشرح بسيط لماذا نحتاج إلى محرر يعرض لنا مكونات أي ملف لدينا, على شكل أرقام بالنظام الـ Hexadecimal, في الحقيقة لم أجد لاسم النظام في رأسي لذلك كتبت المصطلح باللغة الانجليزية.
الميزة أن كل رقم في النظام الـ Hexadecimal يمثل عدد محدد من الـ Bits, بشكل أكثر تحديداً نصف byte أو ما يطلق عليه nibble, و بالتالي فإننا يمكننا أن نغير أي ثابت يحتفظ به البرنامج دون التأثير على بنية البرنامج نفسه أو حجمه. و هذا من الأشياء التي لا يمكن القيام بها في النظام العشري على حد علمي. لأن كل عدد عشري يحتاج إلى عدد غير صحيح لتمثليه على عكس النظام الـ Hexadecimal أو الثماني Octal,
في الحقيقة أن النظام الثماني يوفر هذه الخاصية أيضاً و لكن المبرمجين اعتادوا على النظام الـ Hexadecimal أكثر, لأن كل byte يمكن تمثيله برقمين, بدلاً من تمثيله بثلاثة أرقم كما في النظام الثماني.
ما هي مهمة محرك البرنامج إذاً ؟
مهمة محركنا هو تحويل مصفوفة عددية إلى نص, و هذا النص هو الأعداد نفسها و لكن بالنظام الـ Hexadecimal,
الأعداد في الحاسوب تمثل بالنظام الثنائي, و كل ما تراه على الشاشة هي نصوص و ليست أعداد. صحيح أن الحاسوب على مستوى المعالج يتعامل مع البيانات على شكل أعداد ثنائية, و لكن جميع ما يتم إخراجه للشاشة عبارة عن مجموعة من الحروف, فعلى سبيل المثال لو أن لدينا المثال التالي في ++C :
int x = 15;
cout << x;
ما الذي تعتقد أنه حصل ؟
الكائن cout يقوم بتحويل العدد إلى نص, فالعدد 15 يحتاج إلى أربع bits لتمثيله داخلياً و لكن عند عرضه نحتاج إلى حرفين. ربما الكثيرون في بداية مشوارهم البرمجي واجهتهم مشاريع صغيرة لإنشاء دالة لتحويل عدد ما إلى النظام الثنائي أو الثماني و هكذا....
لماذا أقول حروف و لا أقول 2 bytes, أي أن كل خانة تحتاج إلى byte عندما نحول العدد إلى نص تمهيداً لعرضه.
السبب هو أن تعريف الحرف يختلف من منصة لأخرى! فالترميز الذي نستعمله افتراضياً في برامجنا في ++C, هو ترميز ASCII,
هناك أنظمة أخرى أكثر كفاءة من هذا الترميز و خصوصاً عند الحديث عن لغات غير الانجليزية, أشهر ترميز يسمى UNICODE, في الحقيقة ذكرته هنا, لأن محركنا سيدعم ترميز UNICODE في نسخته القادمة.
بالمناسبة نظام Windows يستعمل ترميز UNICODE بشكل افتراضي في عملياته الداخلية, و أي دالة في Windows تقوم باستقبال نص كوسيط, هذا النص افتراضياً يكون بترميز الـ UNICODE. لذلك سنحتاج إلى إنشاء نسخة أخرى من محركنا تدعم UNICODE بدلاً من ASCII في وقت لاحق.
حتى لا يطول الحديث, تصوري لعملية التحويل يمكن إختزاله في هذا التعريف لدالتنا التي نريد إنشائها.
void convertToHex( unsigned char numbers[], char hexa[], unsigned int numbersCount );
دالتنا في في فكرتها بسيطة جداً! تقوم باستقبال مصفوفة من الأعداد. و سنقوم باستقبال مصفوفة من النوع unsigned char,
يمكن أن نقوم بإعادة تعريف الدالة لكي تستقبل أنواع أخرى, و لكن هذا لا فائدة منه. لسبب بسيط, كل 4 bits من هذه البيانات ستتحول لعدد في النظام الـ Hexadeciaml بغض النظر عن البيانات نفسها.
سأقوم بشرح مثال على إرسال مصفوفات من أنواع أخرى بطريقة بسيطة جداً, ولكن بعد استكمال شرح الدالة.
آخر وسيط هو حجم المصفوفة بالـ bytes. أي حجم المصفوفة []numbers,
الوسيط الذي في المنتصف هو مصفوفة من نوع char, حجمها = numbersCount*2 + 1
بما أن كل 4 bits في الدالة numbers ستتحول لحرف ASCII كامل, سنحتاج إلى 2 bytes لتمثيل كل byte على شكل نص!
و لا تنسى أن لدينا Null Terminator في آخر النص و هذا هو الحرف الزائد في معادلتنا!
كما قلنا أن دالتنا ستسقبل سلسلة من الأعداد, كل عدد عبارة عن byte كامل, لذلك إذا أردنا أن نرسل مصفوفة int تحتوي على عنصرين :
int nums[2] = { 15, 255 };
char hexa[17];
convertToHex( reinterpret_cast<unsigned char*>(nums), hexa, 17 );
std::cout << hexa;
أطمح لإنجاز المشروع كاملاً بإذن الله, فأنا أفكر بتطوير المشروع بحيث يصبح بالإمكان نقله لعدة معالجات, و يسمح بأنظمة ترميز مختلفة, أتكلم هنا عن المحرك و ليس عن البرنامج ككل
إذا يسر الله لنا هذا الأمر سأضع المشروع على sf.net لمن يحتاجه.
في المرة القادمة سأتكلم عن الـ Implementation الذي اخترته و من أين حصلت على الفكرة.
تحياتي,,