شـبـكــة عـمّـــار
إخبارية - ترفيهية
- تعليمية



جديد الصور
جديد الأخبار
جديد المقالات


جديد الصور

جديد البطاقات

جديد الصوتيات

المتواجدون الآن


تغذيات RSS

2012-08-15 06:51

مقدمة



بعيدا عن تعقيد الدرس السابق الذى يكفيك منه بشكل مبدئى أنك عرفت الشكل العام لوحدة DLL وعرفت كيف تنجزها ،لنبدأ فى هذا الدرس معرفة النصف الثاني من القصة التى شرحنا فى الدرس السابق نصفها الأول وهو إنجاز وحدة DLL ، هذا النصف الأخير هو كيفية استدعاء أو استيراد أوتحميل دوال وإجراءات DLL .



تحميل DLLs إلى تطبيقك :



قبل أن تتمكن من استخدام دالة أو إجراء موجود فى DLL فإنك بحاجة إلى تحميل DLL إلى الذاكرة ، تحميل DLL فى RunTime يمكن أن يكون بإحدى طريقتين :



1-التحميل الأستاتيكي Static Loading .



2-التحميل الديناميكي Dynamic Loading .



لكل من الطريقتين حسناتها وعيوبها ،لذا سأوضح الفرق بينهما :



1-التحميل الأستاتيكي:



يقصد به أن ال DLL سيُحمّل أوتوماتيكياً عندما يُنفّذ التطبيق الذي قام باستدعاء DLL ،ولاستخدام هذا النوع من الإستدعاء عليك أن تصرح عن الدالة أو الإجراء الموجود فى DLL باستخدام الكلمة المحجوزة External .



وطالما أن DLL قد حمل عند لحظة تحميل التطبيق بشكل أوتوماتيكي فكل ما عليك فعله لاستدعاء دالة أو إجراء موجود فى DLL هو استدعاء هذه الدالة أو الإجراء بشكل اعتيادي كما تستدعي أي دالة أو إجراء فى Object Pascal .



حسنة هذا النوع من التحميل هى أنه الأسهل للوصول إلى دوال وإجراءات DLL ،وعيبه أن DLL يحمل فى لحظة تحميل البرنامج وبالتالى إذا احتوى DLL على عيوب ما فإن البرنامج سيواجه مشاكل فى تحميله.



2-التحميل الديناميكي:



يقصد به أنك تستطيع تحميل DLL عندما تحتاج إليه وتوقف تحميله عندما لا تتحتاج للتعامل معه كل ذلك أثناء حياة تطبيقك (وجوده بالذاكرة) .



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



العيب الأساسي لاستخدام التحميل الديناميكي أنه يحتاج إلى عمل أكثر من جانبك.



أ-لتحميل DLL ديناميكيا عليك استخدام دالة Windows API التى تدعى Loadlibrary .



ب-عند انتهاء عملك مع DLL استخدم الدالة FreeLibrary لإلغاء تحميله من الذاكرة .



ج-استخدم الدالة GetprocAddress للحصول على مؤشر للدالة أو الإجراء الذى تحتاج لاستدعائه.



استدعاء الدوال والإجراءات الموجودة في DLLs :



كما قلنا سابقاً فإن الطريقة التى تستخدمها لاستدعاء دالة أو إجراء فى DLL تعتمد على ما إذا كان DLL قد حمّل أستاتيكياً أم ديناميكياً .



أولاً الإستدعاء باستعمال التحميل الأستاتيكي :



لاستدعاء دالة أو إجراء ال DLL الذي حمل ستاتيكياً عملية بسيطة ، أولا التطبيق المستدعى يجب أن يحتوي على تصريح عن الدالة أو الإجراء ،بعد ذلك يمكنك استدعاء الدالة أو الإجراء كما تفعل عادةً فى Object Pascal .



لاستيراد دالة أو إجراء محتوى فى DLL استخدم الكلمة المفتاحية External فى تصريح الدالة أو الإجراء ،كمثال لنأخذ الإجراء SayHell الذى رأيناه سابقاً ،التصريح عنه فى التطبيق المستدعى يكون كالتالي:



procedure SayHello(AForm : TForm); external `testdll.dll';



الكلمة المحجوزة External تخبر المترجم أن الإجراء يمكن أن يكون فى DLL (فى حالتنا هذه testdll.dll ) ،
الاستدعاء الفعلي للإجراء( وهو الخطوة التالية وحيثما احتجت إليه) لا يظهر أي اختلاف عن أي إجراء آخر.



SayHello(Self);



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



استخدام الكلمة المحجوزة External :



هذه الكلمة تملك ثلاث حالات لاستيراد إجراء أو دالة من DLL :



1-باستخدام الإسم الفعلي :في هذه الطريقة يمكنك أن تصرح عن إسم الدالة أو الإجراء بالضبط كما جاء في DLL مثلاً :

procedure SayHello(AForm : TForm); external `testdll.dll';



2-باستخدام القيمة الترتيبية :فى هذه الطريقة عليك أن تعين قيمة لترتيب الدالة أو الإجراء كما صدّر من DLL مثلاً:



procedure SomeOrdinalProcedure; external `testdll.dll' index 99;



فى هذا المثال قمت باستيراد الإجراء الذي صدر من DLL عندما index 99 ،أستطيع تسمية الإجراء أي إسم أريده فى التطبيق المستدعي ،
وسيكون المقصود هو ذلك الإجراء فى DLL الذي له نفس قيمة الترتيب المذكور بعد index ، أستطيع تسمية الإجراء أي إسم أريده لأن
الإجراء مصدّر بواسطة قيمة الترتيب وليس بواسطة اسمه .



3-باستخدام إعادة التسمية :تسمح لي هذه الطريقة بأن أستورد الإجراء بواسطة اسمه الأصلي ،لكن أعطي الإجراء اسما جديداً فى التطبيق
المستدعي ، وهذا مشابه للتالي :





procedure CoolProcedure;



external `testdll.dll' name `DoSomethingReallyCool';





هنا استوردت الإجراء المدعو DoSomethingReallyCool وأعدت تسميته باسم CoolProcedure .




ملاحظة/



تعد الطريقة الأولى أي الإستيراد بواسطة الإسم هي المستخدمة عادةً .







كما رأينا فإن التحميل الأستاتيكي يوفر لك كل ما تريده في التعامل مع DLL ، إلا إذا احتجت إلى المرونة التي يوفرها التحميل الديناميكي .




ثانياً استدعاء الدوال والإجراءات باستخدام التحميل الديناميكي :





إن استدعاء الدوال والإجراءات المحملة ديناميكياً يتطلب منك أن تعرف مؤشر للإجراء أو الدالة الموجودة فى DLL (سبق وقدمت شرحا لموضوع
مؤشر إجراء أو دالة في الدرس السابق) ،لشرح هذا الموضوع لنفترض أنك تملك إجراء في DLL يدعى SayHello وهو يمكن أن يظهر كالتالي :




procedure SayHello(AForm : TForm);




begin




MessageBox(AForm.Handle, `Hello From a DLL!',




`DLL Message Box', MB_OK or MB_ICONEXCLAMATION);




end;



لاستدعاء هذا الإجراء من برنامجك ،فإنك يجب أن تصرح عن نوع يصف هذا الإجراء كالتالي :



type



TSayHello = procedure(AForm : TForm);



بعد أن تفعل ذلك ،عليك أن تحمل DLL واستخدام الدالة أو الإجراء ثم إلغاء تحميله عند انتهاء العمل معه (استخدم الدوال المذكورة
فى بداية هذا الدرس لتحقيق ذلك)،
وهذا هو مثالنا كاملاً :



var



DLLInstance : THandle;



SayHello : TSayHello;



begin



{ Load the DLL. }



DLLInstance := LoadLibrary(`testdll.dll');



{ Get the address of the procedure. }



@SayHello := GetProcAddress(DLLInstance, `SayHello');



{ Call the procedure. }



SayHello(Self);



{ Unload the DLL. }



FreeLibrary(DLLInstance);



end;



من الأفضل أن تستخدم code إضافي لفحص الخطأ ، ففي بعض الأحيان تفشل الدالة LoadLibrary في التقاط ممسك ملف DLL ،أو تفشل
الدالة GetprocAddress فى التقاط عنوان الإجراء أو الدالة ، فتلافيا لتوقف البرنامج عند حدوث ذلك نضيف هذا الفحص :



procedure TForm1.DynamicLoadBtnClick(Sender: TObject);



type



TSayHello = procedure(AForm : TForm);



var



DLLInstance : THandle;



SayHello : TSayHello;



begin



DLLInstance := LoadLibrary(`testdll.dll');



if DLLInstance = 0 then begin



MessageDlg(`Unable to load DLL.', mtError, [mbOK], 0);



Exit;



end;



@SayHello := GetProcAddress(DLLInstance, `SayHello');



if @SayHello <> nil then



SayHello(Self)



else



MessageDlg(`Unable to locate procedure.', mtError, [mbOK], 0);



FreeLibrary(DLLInstance);



end;



والآن... بناء تطبيق DLL باستخدام مخزن الأغراض :



لبناء DLL في دلفي اتبع الخطوات التالية :



1-اختار File ثم New لعرض Object Repository ،كما بالشكل التالي:







2-أنقر نقرتين مزدوجتين على أيقونة DLL ، عند ذلك فإن دلفي تبني تطبيق DLL جديد وتعرض محرر الكود كما بالشكل التالي :












الآن يمكنك أن تبدأ بإضافة ال code إلى DLL ،تأكد من إضافة إسم أي دالة أو إجراء مصدرة في جزء Exports ،بعد أن تنتهي من إضافة الكود بإمكانك اختيار Compile أو Built من قائمة Project لبناء DLL .



التعليق الظاهر في وحدة DLL :



بالتأكيد لاحظت التعليق الطويل الذي يظهر فى بداية وحدة DLL ، إن هذا التعليق يخبرك أنه إذا احتوى DLL على دوال أو إجراءات مصدرة تأخذ نوع سلسلة طويلة كمحدد لها ، أو إذا كانت الدالة ترجع نوع سلسلة طويلة فعليك فعل التالي :



1-ضع ShareMem في بداية قائمة Uses في كل من DLL والوحدة الرئيسية للتطبيق المستدعي ،تأكد من أن ShareMem تأتي قبل الوحدات الأخرى فى قائمة Uses .



2-إشحذ ملف Borlandmm.dll مع DLL ،لاحظ أني قلت Borlandmm.dll وليس Delphimm.dll كما أشار التعليق الموجود في DLL ،ذلك أن Borland غيرت إسم مدير ذاكرة DLL لكنها أهملت تغيير التعليقات المنتجة عندما تبني DLL جديد ،ومع ذلك فإن التعليق ليس مضللا بالكامل ، لأن كلا من Delphimm.dll و Borlandmm.dll مضمنان في Delphi4 .



لتجنب هذه الطلبات ، فقط تأكد من أن دوالك وإجراءاتك في DLL لا تأخذ محددات نوع سلسلة طويلة ودوال DLL لا ترجع سلسلة طويلة ، بدلا عن استخدام سلسلة طويلة (Long String ) بإمكانك استخدام Pchar String أو Short String مثلا : عوضا عن استخدام :



procedure MyProcedure(var S : string);



begin



{ Procedure code here. }



end;



إستخدم:







procedure MyFunction(S : PChar);



begin



{ Procedure code here. }



end;



هذا الأمر سهل الإستخدام لذلك أنت لا تحتاج مطلقا إلى Borlandmm.dll ، أنت فقط بحاجة لأن تعي القيود الموضوعة على استخدام السلاسل الطويلة في دوال وإجراءات DLL ، لاحظ أنك تستطيع استخدام السلاسل الطويلة في الدوال والإجراءات المستخدمة داخلياً في DLL ،إذاً القيود السابقة تنطبق فقط على الدوال والإجراءات المصدرة.



ملاحظة :



بعد أن فهمت ما يعنيه التعليق بإمكانك مسحه ، لأنه لم يعد ضرورياً بالنسبة لك.



تطبيق عملي:



إليك الآن هذه الأكواد الثلاثة التي تعطي مثالاً للمفاهيم المناقشة سابقاً ،الأول يحتوي DLL سيتم استدعاؤه ستاتيكياً من التطبيق المستدعي ،الثاني يحتوي DLL سيستدعى ديناميكياً من الإجراء المستدعي بأدوات DLLProc ،أما الثالث فهو يحتوي البرنامج الذي يستدعي DLL السابقين ،البرنامج يملك form مع أربع buttons التى تستدعي إجراءات متعددة موجودة في ملفيDLLs .



TestDll.dpr.

library TestDLL;



uses



SysUtils,



Classes,



Forms,



Windows;



procedure SayHello(AForm : TForm);



begin



MessageBox(AForm.Handle, `Hello From a DLL!',



`DLL Message Box', MB_OK or MB_ICONEXCLAMATION);



end;



procedure DoSomething;



begin



MessageBox(0, `This procedure was exported by ordinal.',



`DLL Message Box', MB_OK or MB_ICONEXCLAMATION);



end;



procedure DoSomethingReallyCool;



begin



MessageBox(0, `Something really cool.',



`DLL Message Box', MB_OK or MB_ICONEXCLAMATION);



end;



exports



SayHello,



DoSomething index 99,



DoSomethingReallyCool;



begin



end.



DynLoad.dpr.

library TestDLL;



uses



SysUtils,



Classes,



Forms,



Windows;



procedure MyDLLProc(Reason: Integer);



begin



if Reason = DLL_PROCESS_DETACH then



{ DLL is unloading. Cleanup code here. }



MessageBox(0, `DLL is unloading!',



`DLL Message', MB_OK or MB_ICONEXCLAMATION);



end;



procedure SayHelloDyn(AForm : TForm);



begin



MessageBox(AForm.Handle, `Hello From a DLL!' + #13 +



`This DLL was loaded dynamically',



`DLL Message', MB_OK or MB_ICONEXCLAMATION);



end;



exports



SayHelloDyn;



begin



DLLProc := @MyDLLProc;



end.



CallDllU.pas.

unit CallDLLU;



interface



uses



Windows, Messages, SysUtils, Classes, Graphics,



Controls, Forms, Dialogs, StdCtrls;



type



TForm1 = class(TForm)



HelloBtn: TButton;



OrdBtn: TButton;



DynamicLoadBtn: TButton;



NamedBtn: TButton;



procedure HelloBtnClick(Sender: TObject);



procedure OrdBtnClick(Sender: TObject);



procedure DynamicLoadBtnClick(Sender: TObject);



procedure NamedBtnClick(Sender: TObject);



private



{ Private declarations }



public



{ Public declarations }



end;



var



Form1: TForm1;



{ A procedure imported by name. }



procedure SayHello(AForm : TForm);



external `testdll.dll';



{ A procedure imported by ordinal. }



procedure OrdinalProcedure;



external `testdll.dll' index 99;



{ A procedure imported and renamed. }



procedure CoolProcedure;



external `testdll.dll' name `DoSomethingReallyCool';



implementation



{$R *.DFM}



procedure TForm1.HelloBtnClick(Sender: TObject);



begin



SayHello(Self);



end;



procedure TForm1.OrdBtnClick(Sender: TObject);



begin



OrdinalProcedure;



end;



procedure TForm1.NamedBtnClick(Sender: TObject);



begin



CoolProcedure;



end;



procedure TForm1.DynamicLoadBtnClick(Sender: TObject);



type



TSayHello = procedure(AForm : TForm);



var



DLLInstance : THandle;



SayHello : TSayHello;



begin



{ Load the DLL. }



DLLInstance := LoadLibrary(`DynLoad.dll');



{ If loading fails then bail out. }



if DLLInstance = 0 then begin



MessageDlg(`Unable to load DLL.', mtError, [mbOK], 0);



Exit;



end;



{ Assign the procedure pointer. }



@SayHello := GetProcAddress(DLLInstance, `SayHelloDyn');



{ If the procedure was found then call it. }



if @SayHello <> nil then



SayHello(Self)



else



MessageDlg(`Unable to locate procedure.', mtError, [mbOK], 0);



{ Unload the DLL. }



FreeLibrary(DLLInstance);



end;



end.




الخلاصة:



رأينا في هذا الدرس كيف نحمل وحدة DLL ثم كيف نستدعي دالة أو إجراء من DLL واتجهنا إلى التطبيق العملي لإيضاح بناء واستدعاء DLL .



إلى هنا نكون قد غطينا الجزء الأكبر من دراستنا لل DLL حيث أننا أصبحن قادرين على إنشائه والتعامل معه.



تعليقات 0 | إهداء 0 | زيارات 678


خدمات المحتوى
  • مواقع النشر :
  • أضف محتوى في Digg
  • أضف محتوى في del.icio.us
  • أضف محتوى في StumbleUpon
  • أضف محتوى في Google


تقييم
3.08/10 (9 صوت)


Powered by Dimofinf cms Version 3.0.0
Copyright© Dimensions Of Information Inc.