منتدي ديفل ايز
اهلا وسهلا بك اخي الزائر يشرفنا ان تكون احد تعضاء المنتدي الكرام وشكرا لك

انضم إلى المنتدى ، فالأمر سريع وسهل

منتدي ديفل ايز
اهلا وسهلا بك اخي الزائر يشرفنا ان تكون احد تعضاء المنتدي الكرام وشكرا لك
منتدي ديفل ايز
هل تريد التفاعل مع هذه المساهمة؟ كل ما عليك هو إنشاء حساب جديد ببضع خطوات أو تسجيل الدخول للمتابعة.

كيف تصنع مكتبة ملفات

اذهب الى الأسفل

كيف تصنع مكتبة ملفات  Empty كيف تصنع مكتبة ملفات

مُساهمة  Admin الثلاثاء نوفمبر 09, 2010 12:46 am

فى هذا الدرس

* ماهى المكتبات؟ و ما الهدف منها؟
* انواع المكتبات
* كيفية انشاء مكتبه بسيطه
* التصدير بإستخدام __declspec
* التصدير بإستخدام Module-Definition File
* الإختلاف بين المكتبات الإستاتيكيه و الديناميكيه



1 - ماهى المكتبات؟ و ما الهدف منها؟
المكتبه هى مجموعه من الدوال و الفئات التى تؤدى عمليات معينه و يتم تجميعهم معا فى مكان واحد (المكتبه) لإنهم يؤدوا وظائف متجانسه (فكرتونى بأيام حصة العربى لما كان المدرس بيقولنا عرف كذا :wacko: ).

الهدف من المكتبه انت الذى تعرفه لإنك من تريد صنعها Smile.

2 - انواع المكتبات
للمكتبات نوعين الأول هو المكتبات الإستاتيكيه و الثانيه هى مكتبات الربط الديناميكى. لكل نوع خصائص و وقت معين ليتم استخدامه فيه و سيتم معرفة الفرق بينهم فيما بعد.

3 - كيفية انشاء مكتبه بسيطه
لإنشاء مكتبه بسيطه قم بفتح الفجوال ستديو و اختر مشاريع الـ Cpp ثم اختر Win32 و من الجانب الأخر اختر Win32 Project و اكتب اسم المشروع (SimpleDll) ثم اضغط OK.

من الشاشه تاليه الظهور اضغط Next و قم بتحديد DLL ثم قم بتحديد Empty Project و اضغط Finish ليتم انشاء مشروع مكتبه فارغ.

قم بتحديد Header Files و من قائمة Project اختر Add New Item و من الشاشه التى ستظهر لك قم بتحديد Header File و اعطه الإسم MyMethods و اضغط OK ثم افتح الملف و اكتب بداخله الكود التالى:

الكود:
#ifndef MY_METHODS_H
#define MY_METHODS_H


extern "C" int __stdcall Sqr(int i);


#endif // MY_METHODS_H



الملف MyMethods.h يحتوى الأن على تصريح لداله اسمها Sqr هذه الداله يمكن استدعائها من لغة الـ C و كل ما تفعله هو حساب تربيع لرقم بالنسبه للـ __stdcall سأتكلم عنها لاحقا.

الأن قم بتحديد Source Files و قم بإضافة ملف C++ File و اسمه MyMethods و اكتب داخله الكود التالى

#include "MyMethods.h"
#include

int __stdcall Sqr(int i)
{ return i*i; }

BOOL WINAPI DllMain(void* DllHandle, unsigned Reason, void *Reserved)
{
return TRUE;
}



كما ترى فقد قمنا بتضمين الداله Sqr و كذلك قمنا بكتابة داله اخرى و هى DllMain، هذه الداله هى نقطة البدء للمكتبه او الـ EntryPoint و يتم تمرير لهذه الداله 3 معاملات و هم:

* DllHandle: مقبض المكتبه و الذى يتم الحصول عليه بإستخدام LoadLibrary.
* Reason: و هو من قام بتحميل المكتبه و يتم تمرير لها قيمه من نظام التشغيل عن تحميلها و هى واحده من 4 و هم:
o DLL_PROCESS_ATTACH : المكتبه تم تحميلها من الـ Thread الرئيسى بالبرنامج الحاوى للمكتبه.
o DLL_DETACSS_DETACH : المكتبه تم تحريرها من الـ Thread الرئيسى بالبرنامج الحاوى للمكتبه.
o DLL_THREAD_ATTACH : المكتبه تم تحميلها من Thread فرعى بالبرنامج الحاوى للمكتبه.
o DLL_THREAD_DETACH : المكتبه تم تحريرها من Thread فرعى بالبرنامج الحاوى للمكتبه.

* Reserved : محجوزه لإستخدام نظام التشغيل.



الداله DllMain لابد ان تعود بـقيمه و هى اما TRUE = 1 و تعنى نجاح تحميل المكتبه و بهذا سيتم الرجوع بمقبض لها من الداله LoadLibrary او FALSE = 0 و تعنى فشل تحميل المكتبه و بهذا ستعود الداله LoadLibrary بصفر.

يقوم نظام التشغيل بتحميل اى مكتبه مره واحده فقط و بعد ذلك فى كل مره يتم استدعاء الداله LoadLibrary يتم زيادة عداد داخلى لهذه الكتبه بواحد و عند استدعاء الداله FreeLibrary يتم انقاص هذا العداد بواحد حتى ان اصبح بصفر يتم تحرير المكتبه من الذاكره.

مما سبق نستنتج ان لأى مكتبه تم تحميلها داخل الذاكره يصبح لها مقبض واحد فقط و هو الذى يتم العوده به دائما حتى يتم تحرير هذه المكتبه من الذاكره.

قم بعمل Build للمشروع و ستجد ان المكتبه قد تم عملها.

4 - التصدير بإستخدام __declspec
تحتوى المكتبه الأن على الداله Sqr و لكن للأسف لن تستطيع استدعائها و السبب فى ذلك لأنه لم يتم تصدير الداله للإستخدام الخارجى. التصدير الخارجى المقصود به هو اتاحة برامج اخرى من استخدام دوال قمنا بكتابتها داخل مكتبه من صنعنا (مثل الموجود بنظام التشغيل).

لذا كيف نقوم بتصدير دوال خارج مكتبتنا؟
الأمر بسيط فقط قم بتعديل صيغة الداله الموجوده داخل الـ header لتصبح كالتالى:

extern "C" __declspec(dllexport) int __stdcall Sqr(int i);


عدل سابقا من قبل Admin في الخميس ديسمبر 30, 2010 10:31 am عدل 1 مرات
Admin
Admin
Admin

عدد المساهمات : 151
نقاط : 412
تاريخ التسجيل : 02/09/2010

http://pirates.biz

الرجوع الى أعلى الصفحة اذهب الى الأسفل

كيف تصنع مكتبة ملفات  Empty رد: كيف تصنع مكتبة ملفات

مُساهمة  Admin الثلاثاء نوفمبر 09, 2010 12:50 am


كما تلاحظ تم اضافة الكلمه __declspec(dllexport) فماذا تعنى:
تنقسم هذه الكلمه إلى 3 اقسمام هم decl و spec و dllexport :

الكلمه الأولى و الثانيه مجتمعين معا يكونوا الكلمه : declaration specifier و هى تتبع إمتدادات اللغه لميكروسوفت و تستخدم لإعلام الـ Linker بمعلومات إضافيه عن المكان الذى تم كتابته فيه (و تكتب بجانب دوال و متغيرات و فئات).
الكلمه الثالثه و هى dllexport و تعنى وضع ما يلى داخل جدول التصدير للملف التنفيذى (الملف التنفيذى قد يكون اى ملف له PE Tag).

قم بعمل Build للمكتبه ستجد انه تم تصدير الداله بالإسم _Sqr@4 و هو كما ترى ليس الإسم الذى قمنا بتحديده داخل ملف الـ header و يؤسفنى ان اقول لك انه حتى هنا لا يمكنك فعل اى شئ لتغيير هذا الإسم إلا بإستخدام برامج خارجيه اما ان تقوم بتحديد الإسم من داخل البرنامج فهذا يفوق امكانيات الكلمه declspec و يؤدى بنا إلى النقطه التاليه.

5 - التصدير بإستخدام Module-Definition File
كما عرفنا ان الكلمه __declspec(dllexport) تخاطب الـ Linker و لكن الرساله التى تقولها له محدودة التفاصيل فهى كل ما تفعله هو انها تحدد ان هذا العنصر سيتم تصديره لخارج المكتبه و لا تسمح لنا تحديد اى معلومات اخرى لـ Linker عن هذا العنصر و من هنا تحتم وجود ملف الـ Defination.

ملف الـ Defination هو ملف يحتوى على معلومات خاصه بالمكتبه منها معلومات خاصه بتصدير الدوال خارج المكتبه و يتيح لك هذا الملف تحديد معلومات اخرى عن الداله مثل ترتيب تصديرها او تصديرها برقم و ليس بإسم او الـ Redirection لدوال من مكتبه اخرى داخل مكتبتك و سنعرف كيفية عمل كل ذلك فيما يلى:

اولا قم بحذف الكلمه __declspec(dllexport) من تعريف الداله Sqr حيث انه لا داعى لها الأن قم بإضافة ملف Module-Defination File(.def) و قم بتسميه exports.def
بعد إضافة الملف قم بفتحه لتجد به البيان التالي

الكود:
LIBRARY "SimpleDll"

هذا البيان يعنى ان كل مايلى هو متعلق بالمكتبه SimpleDll الأن قم بإضافة الأوامر التاليه بعد البيان السابق
EXPORTS
Sqr @1

الكود:

كما ترى قمنا بإضافة section بإسم exports و هو يخاطب الـ linker بأن كل ما يوجد اسفله هى دوال سيتم تصديرها من هذه المكتبه، بعد ذلك قمنا بإضافة صيغه غريبه بعض الشئ و هى


الصيغه السابقه تعنى ان الداله Sqr هى داله موجوده فى هذه المكتبه و اسمها هو Sqr (لاحظ ان اسماء الدوال هى case-sensitive) و سيتم وضعها داخل جدول الـ export و رقمها هو 1.

بعد بناء المكتبه إذا قمت فتحها بأى برنامج PE Editor ستجد انه داخل جدول التصدير توجد الداله Sqr و ترتيبها داخل الجدول هو الأول.

جميل اذا ماذا يحدث فى حال وجود اكثر من داله: الحل بسيط قم بإضافة الداله التاليه للمكتبه (التصريح داخل الـ header و الكود داخل ملف الكود)

bool __stdcall IsNegative(int i);

bool __stdcall IsNegative(int i)
{ return i<0; }



بعد ذلك قم بوضع اسم الداله داخل ملف exports.def و ضع لها رقم كالتالى

[code]LIBRARY "SimpleDll"
EXPORTS
Sqr @1
IsNegative @2

قد يتسائل احدكم ما فائدة هذا الرقم و هل هو اجبارى بمعنى اذا حذفته ماذا سيحدث؟ الإجابه بسيطه جرب (انا نفسى لم اكن اعرف اذا حذفته ماذا سيحدث حتى كتابة هذا السؤال)؟
بالنسبه لفائدة الرقم فهو مفيد جدا، صراحة لا اريد جعل الدرس معقد و لكنى سأجيب هذا السؤال فى اخر الدرس (بهذا اصبحوا سؤالين الأول الخاص بالـ Call Convention و هذا هو الثانى).
بالنسبه لكونه اجبارى فلا هو ليس اجبارى فإن حذفته سيتم تصدير الدوال بالترتيب الأبجدى مثلا هنا قمنا بجعل ترتيب الداله Sqr هو الأول و IsNegative هو الثانى فإذا قمت بحذف الرقم ستصبح الداله IsNegative هى الأولى يليها الداله Sqr حيث ان حرف الـ I يأتى اولا يليه S فى الترتيب الأبجدى.

سؤال اخر فالنفترض ان الداله موجوده داخل ملف الـ Header بإسم و تريد انت تصديرها بإسم اخر فما الحل بمعنى قم بإضافة هذه الداله (مثل السابق قم بتوزيع الكود و العريف كل فى مكانه)
expand | plain text

extern "C" int __stdcall CubeInternal(int i);

int __stdcall CubeInternal(int i)
{ return i*i*i; }


قبل ان اكمل قد يتسأل البعض ما الهدف من اعادة تسمية داله عند تصديرها، حسنا احيانا تقوم بعمل بعض الدوال بأسماء ذات شكل غريب و اقصد نظام معين لتسمية الدوال الداخليه و بعد فتره اصبح اعتمادك على احدى هذه الدوال اساسى لدرجه تحتم عليك انك تحتاج لتصديرها لذا امامك حل من اثنان اما ان تقوم بتعديل اسم الداله داخل المشروع بأكمله (و لا تنسى قد يكون هناك مكتبات اخرى تستخدم هذه الداله) و هذا قد يتسبب فى انهيار النظام (نظام برامجكك الذى يستخدم فيه هذه المكتبه) او ن تقوم فقط بتصديرها بإسم أخر و هذا هو الحل الذى يفضله اغلب المبرمجين.

لنكمل الداله CubeInternal تقوم بحساب مكعب رقم و هذه الداله داله داخليه فى المكتبه و غير مسموح بإستخدامها خارجا و لكن بعد فتره من الزمن وجدت انها ستكون ذات فائده ان قمت بتصديرها لذا فالحل امامك هو تصديرها بإسم مختلف و ذلك يتم عن طريق اضافتك لها داخل ملف exports بالصيغه التاليه:
expand | plain text

Cube = CubeInternal


كما تلاحظ على الجانب الأيسر من علامة التساوى هو الإسم الذى سيتم به تصدير الداله خارج المكتبه و على الجانب الأيمن الإسم الداخلى للداله لذا يصبح ملف exports شكله كالتالى
expand | plain text

LIBRARY "SimpleDll"
EXPORTS
Sqr @1
IsNegative @2
Cube = CubeInternal @3



فى بعض الأحيان تلح الحاجه إلى تصدير دوال Private و لكن فى نفس الوقت لا تريد لعملائك ان يستخدموها لإنه قد يتم حذفها او تعديلها فى اى وقت لذا فتصديرها بالإسم قد يسبب مشكله و الحل المتاح امامنا هو تصديرها فقط برقم و ليس بإسم، ما اقصد برقم ليس ان تقوم بإعادة تسميتها و الإسم الجديد يكون رقم، لا هذا ليس ما اقصده و لكن ما اقصده هو ان يتم تصدير الداله و يتم التعامل معها برقم ترتيبها داخل جدول الدوال التى تم تصديرها. هذا النوع من الدوال تسمى Ordinal Exported Function.

كمثال على هذا النوع من الدوال قم بإضافة الداله التاليه (مش هفضل اقولك كل شويه اتعود بقى)
expand | plain text

extern "C" bool __stdcall IsNanInternal(float f);


bool __stdcall IsNanInternal(float f)
{
unsigned i = *(unsigned*)&f;
return i == 0x7fffffff || // sNan
i == 0x7fc00000 || // qNan
i == 0xffffffff || // -sNan
i == 0xffc00000; // -qNan
}


الداله IsNanInternal تقوم بالتحقق من ان قيمة متغير float هى Nan، هذه الداله نحن مجبرين ان نقوم بتصديرها لإستخدامها داخل مكتبه اخرى فى حين اننا لا نريد للعميل ان يستخدمها و اذا قمنا بتصديرها بالإسم فلابد من وضع بيان لها فى الوثائق و الإسم فى حد ذاته قد يفضح امرها لذا لتجنب تلك الأمور سنقوم بتصديرها بترتيب رقمها داخل جدول التصدير و ذلك عن طريق اضافة السطر التالى داخل ملف الـ exports.def
expand | plain text

IsNanInternal @4 NONAME


الكلمه NoName تأتى بعد الرقم (إن كان موجود) و تعلم الـ Linker ان هذه الداله سيتم تصديرها و لكن لن يتم وضع اسم لها داخل الـ export table.

يتبقى لنا نقطه لم نتحدث عنها و هى الـ Redirection.

الـ Redirection او تحويل الدوال هو تصدير داله من مكتبتك لم يتم تعريفها او تضمينها داخل مكتبتك و لكن تم تعريفها و تضمينها داخل مكتبه اخرى و ذلك يتم عن طريق الـ Static Linking مع هذه المكتبه بمعنى المكتبه A تحتوى على الداله Test و انت قمت داخل المشروع بتحديد Additional Libraries للـ Linker للمكتبه A لذا إذا قمت بكتابة Test داخل ملف الـ exports.def الموجود بمشروعك فسيقوم الـ Linker بشكل تلقائى بإضافة كود لإستدعاء للداله Test الأصليه عند استدعائك للنسخه الموجوده بمكتبتك.

كما ان الـ Linker مرن جدا فى هذا الأمر فهو يسمح لك بتغيير اسم الداله Test داخل مكتبك بمعنى ان الداله موجوده بالمكتبه A بإسم Test و انت يمكنك اعادة تصديرها من مكتبتك بإسم MyTest و ايضا يمكنك اعادة تصديرها من داخل مكتبتك بترتيب رقمها فقط (داخل مكتبتك انت).

و كمثال عملى سنقوم بإعادة تصدير الداله GetProcessHeap المعرفة بـ Kernel32.lib من داخل مكتبتنا بإسم اخر وهو GetMyHeap كالتالى
expand | plain text

GetMyHeap = GetProcessHeap @5



نقطه اخيره نسيت ذكرها و هى انه يمكنك كتابة ملاحظات داخل ملف الـ Defination و لفعل ذلك فقط قم بوضع semi-colon قبل الملاحظات و اعلم ان بدءا من مكان ظور الـ semi-colon لأخر السطر يعتبر ملاحظه.

ملف الـ Exports الخاص بنا شكله النهائى كالتالى
expand | plain text

LIBRARY "SimpleDll"
EXPORTS
;
; MyMethods.cpp - General Functions
;
Sqr @1
IsNegative @2
Cube = CubeInternal @3
IsNanInternal @4 NONAME ; private function

;
; Redirected functions
;
GetMyHeap = GetProcessHeap @5



6 - الإختلاف بين المكتبات الإستاتيكيه و الديناميكيه
و صلنا لأخر نقطه و هى الإختلاف بين المكتبات الإستاتيكيه و المكتبات الديناميكيه.

المكتبه الديناميكيه هى فعليا ملف تنفيذى لإنها تحتوى على نظام الملفات التنفيذيه PE و الذى يمكن للـ Loader الخاص بنظام التشغيل تحميله فى الذاكره و تنفيذه.

اما المكتبات الإستاتيكيه فهى ليست ملفات تنفيذيه و تحتوى اما على بيانات الدوال و الكود او بيانات الدوال و مؤشر لمكان الكود داخل مكتبه ديناميكيه فإن كانت من النوع الأول فعند الربط مع الملف التنفيذى (ايا كان امتداده) يتم نسخ الكود بأكمله داخل الملف التنفيذى و إذا كان النوع الثانى يتم الربط على الداله بداخل المكتبه الديناميكيه.

و بذلك نكون قد انتهينا من الدرس و بقي سؤالين سأجيب عليهم الأن.

السؤال الأول معنى الكلمه __stdcall : يوجد موضوع جيد جدا على موقع Code Project يشرح معناها.

السؤال الثانى ما هو فائدة تصدير الدوال بترتيب معين(اى بكتابة الترتيب بشكل صريح): الإجابه هى تعتمد اولا و اخيرا على صانع المكتبه فأنا مثلا قد اقوم بترتيب الدوال المصدره حسب عدد و نوع المعاملات و بذلك استطيع عند تحميل المكتبه ان اضع كل الدوال ذات المعاملات المتشابه فى النوع و العدد داخل مصفوفة دوال واحده و استدعى الداله بالـ Index الخاص بها داخل المصفوفه و هذا الأمر يتيح لى مرونه و سرعه فى التعامل مع هذه الدوال.
Admin
Admin
Admin

عدد المساهمات : 151
نقاط : 412
تاريخ التسجيل : 02/09/2010

http://pirates.biz

الرجوع الى أعلى الصفحة اذهب الى الأسفل

الرجوع الى أعلى الصفحة


 
صلاحيات هذا المنتدى:
لاتستطيع الرد على المواضيع في هذا المنتدى