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



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


جديد الصور

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

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

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


تغذيات RSS

2012-08-15 06:50

طريقة إنجاز وحدة DLL فى لغة الدلفي



مقدمة:



وحدة DLL تختلف عن بقية وحدات دلفى، سنرى فى هذا الدرس أوجه الإختلاف وما هى الأمور التى يجب مراعاتها عند بناء هذه الوحدة، وأنواع الدوال والإجراءات فى DLL ،إضافة إلى موضوع مهم وهو العلاقة بين DLL وبعض متغيرات النظام Windows العامة .



تحليل وحدة DLL :



كأي وحدة من وحدات Pascal الأخرى فإن DLL Unit لها شكلها المستقل ، ولدراسة الشكل العام لهذه الوحدة لنستخدم المثال التالي :



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;
exports
SayHello;
begin
end.
1-الكلمة المحجوزة Library : تستخدم فى بداية الوحدة ، ويليها إسم الوحدة وهو يخضع لاختيارك الشخصي هنا (TestDLL ) أو أي تسمية أخرى تخضع لشروط التسمية فى Pascal .



2-الكلمة المحجوزة Uses : تأتي هذه الكلمة متبوعة بأسماء الوحدات المراد تضمينها فى هذه الوحدة.



3-الدوال والإجراءات : قائمة بتعريفات الدوال والإجراءات التى تحتويها وحدة DLL .



4-الكلمة المحجوزة Exports : وتأتي متبوعة بالدوال والإجراءات المصدرة (من بين الدوال والإجراءات التى سبق تعريفها) إلى خارج وحدة DLL .



5-الكلمتان المحجوزتان Begin و End : يوضع بينهما كتلة الكود الرئيسي لل DLL ، حيث تضع هنا أي كود تحتاج لتنفيذه عند لحظة تحميل DLL ، فى معظم الحالات أنت لا تحتاج لأي كود استهلالي وتكتفي بالدوال والإجراءات وبالتالى تبقى هذه الكتلة فارغة .



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



أساسيات كتابة DLL :



إن كتابة DLL ليست عملية صعبة ويمكن اعتبارها كبرمجة Object Pascal المعتادة ، لكن هناك عدة أمور هامة يجب عليك إدراكها ،بعدها يمكنك كتابة أول DLL .



الدوال والإجراءات فى DLL : تنقسم الدوال (Functions ) والإجراءات (Procedures ) فى DLL إلى قسمين :



1-دوال وإجراءات محلية لل DLL .



2-دوال وإجراءات مصدرة إلى خارج ال DLL .



أولاً/ الدوال والإجراءات المحلية:



هى الدوال والإجراءات التى سيتم استدعاؤها داخل ال DLL نفسه . هى لا تتطلب معالجة خاصة عند إنجازها ،كل ماعليك فعله هو تعريف هذه الدوال والإجراءات كأي دالة أو إجراء آخر فى Object Pascal .



ويتم استدعاؤها من قبل دوال وإجراءات أخرى داخل ال DLL ، لكن لا يمكن استدعاؤها من خارج ال DLL ، بكلمات أخرى التطبيقات المستدعية لا يمكنها الوصول إلى هذه الدوال والإجراءات وهى تكون Private لل DLL .



ملاحظة: سيتكرر استخدامي للتعبير التطبيقات المستدعية (Calling Applications ) وأقصد به تلك التطبيقات التى تستدعى دالة أو إجراء من دوال وإجراءات ال DLL .



تلميح:إضافة للدوال والإجراءات فإن DLL يمكن أن يحوى بيانات عامة Global Data تستطيع كل الإجراءات والدوال الموجودة فى DLL الوصول إليها.



فى Windows 16_bit فإن البيانات العامة فى DLL مشتركة فيما بين كل مكونات DLL،فإذا قام برنامج ما بتغيير قيمة المتغير العام X مثلا إلى 100 فإن X سيمتلك القيمة 100 بالنسبة لكل التطبيقات التى تستخدم هذا ال DLL

فى Windows 32_bit الأمر مختلف حيث أن كل إجراء يصل إلى DLL يُبنى له نسخة مستقلة من البيانات العامة .






ثانياً/الدوال والإجراءات المصدَّرة من DLL :



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



ولتصدير الدوال والإجراءات تستخدم الكلمة المحجوزة Exports وهذا هو الفرق بينها وبين الدوال والإجراءات المحلية لل DLL .



الكلمة المحجوزة Exports :



كما قلنا سابقا لتصدّر دالة أو إجراء إلى خارج DLL فإنك تستخدم الكلمة المحجوزة Exports ، أنظر إلى مثالنا في بداية الدرس ،لاحظ كيف قمنا بتعريف الدالة SayHello على أنها مصدّرة ،وبالتالى يمكن استدعاؤها من أي تطبيق دلفي آخر .



ولتصدير دالة أو إجراء هناك طريقتين :



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



exports
SayHello,
DoSomething,
DoSomethingReallyCool;
هذه الدوال صدّرت باستخدام إسم التعريف لها وهو الإسم الوارد فى تعريف الدالة السابق ،ونلاحظ أن جزء Exports له نفس ( Syntax ) قاعدة القائمة Uses حيث أنه فصل بين المعرفات باستخدام فاصلة عادية ووضعت فاصلة منقوطة بعد آخر معرّف في القائمة .



2-التصدير باستخدام قيمة الترتيب : يمكن أيضاً تصدير الدوال والإجراءات بواسطة قيمة ترتيبها ،وتستخدم لذلك الكلمة المحجوزة Index مثال :



exports
SayHello index 1,
DoSomething index 2,
DoSomethingReallyCool index 3;
عندما تقوم باستيراد الدالة فى التطبيق المستدعى عليك تحديد قيمة الترتيب
عندما تقوم باستيراد الدالة فى التطبيق المستدعى عليك تحديد قيمة الترتيب (Ordinal number)

فى معظم الحالات يتم استخدام الإسم لتصدير الدالة.



ملاحظة: فى حالة عدم تحديدك لقيمة ترتيب الدوال والإجراءات الموجودة فى جزء Exports فإن دلفى تقوم أوتوماتيكياً بإسناد قيمة ترتيب لكل إجراء أو دالة فى هذا الجزء ،لكن استخدامك Index يوفر لك تحكماً فى قيمة Ordinal Number التى تضعها حسب رغبتك.



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

العلاقة بين DLLs ومتغيرات النظام Windows :

هذا الجانب يعد مهماً لكنه معقد بعض الشيء ، للنظام Windows عدد من المتغيرات العامة المتعلقة بDLL والتى يمكن رؤيتها من تطبيقك أو من DLL وهي:

1-Islibrary : يحدد هذا المتغير ما إذا كان كودك ينفذ في DLL أو فى التطبيق ،وقيمته دائماً False فى التطبيق و True فى DLL .



2-HInstance : يحتوى قيمة (Instance handle ) ممسك DLL ،الممسك هو أحد أنواع البيانات المعرفة فى الوحدة Windows وهى تنفذ كأرقام ،لكنها لا تستعمل كذلك ولكنها إشارة إلى بنية بيانات داخلية للنظام ،مثلاً عندما تتعامل مع نافذة أو نموذج (Form ) فإن النظام يعطيك ممسك للنافذة ،النظام يخبرك أن النافذة التى تتعامل معها هى نافذة رقم 142 مثلاً ، وبالتالي فالممسك هو توليف داخلي يمكنك استعماله لتشير به إلى عنصر معين يتم مناولته من قبل النظام ،وهذا الأمر ينطبق على DLL حيث أنه كما قلنا المتغير العام Hinstance يحتوى ممسك ال DLL .



3-DLLProc : (هذا أكثر المتغيرات أهمية)، كما لاحظت عند تحليلنا لوحدة DLL فإنها لا تحتوي على جزء initialization أو جزء finalization عكس بقية الوحدات الأخرى فى باسكال ،إذاً ماذا يمكنك أن تفعل لتجعل كود معين ينفذ عند لحظة تحميل DLL ؟ ،أو عند إلغاء تحميله ؟، أو عند تصدير المعالجات أو استلامها من DLL محمل فى الذاكرة (هنا حالة استخدام DLL من قبل عدة تطبيقات فى ذات الوقت ،أو استخدامه من عدة مواضع فى ذات التطبيق) ؟



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



إن DLLProc هو متغير من نوع إجراء (وبتحديد أكثر مؤشر إلى إجراء).



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



ماهو المتغير من النوع الإجرائي ؟



من الميزات الفريدة فى Object Pascal هى وجود الأنواع الإجرائية Procedure Types ،وهو موضوع برمجي متقدم ( يشبه مفهوم مؤشر الوظيفة Function Pointer فى لغة C ) .



تعريف النوع الإجرائي يشير إلى قائمة من المحددات ونوع المرجوع فى حالة الدوال ،مثلاً يمكنك تعريف نوع إجراء مع محدد برقم صحيح يتم تمريره بالإشارة مثل :



Type



Intproc = procedure (var num : integer);



-Intproc :النوع الجديد الذى تقوم بتعريفه تليه علامة المساواة.



-الكلمة المحجوزة Procedure فى حالة الإجراء ،أو الكلمة المحجوزة Function فى حالة الدالة .



-قائمة المحددات وأنواعها ،يمكنك تمرير المحددات بشكل اعتيادى كما فى أى إجراء أو دالة فى باسكال.



-فى حالة الدالة عليك ذكر نوع مرجوعها، مثلاً



Type



sumfunc = function (num1,num2 : integer):integer;







ملاحظة: يمكنك تعريف متغير من نوع إجراء مباشرة فى جزء var دون الحاجة إلى تعريف نوع عنه مثل:



var







F: function (X: Integer): Integer;



بالعودة إلى النوع intproc الذي عرفناه سابقاً نقول أنه متوافق مع أي إجرائية تملك نفس المحددات ،يمكنك تمرير المحددات بشكل اعتيادي كما فى أي إجراء أو دالة فى باسكال ،وهذا مثال لإجرائية متوافقة مع نوع إجراء السابق:

Procedure DoubleTheValue (var value : integer

Begin



Value:=value*2 ;



End ;



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

Var



IP: intproc ;



X : integer ;



Begen



IP:= DoubleTheValue ;



X:= 5 ;



IP(X);
لمعرفة المزيد عن النوع الإجرائي بإمكانك مراجعة " procedural types " فى مساعدة دلفى.



والآن لنعود إلى موضوعنا وهو DLLProc ، قلنا أن DLL يستلم رسائل من النظام Windows عند لحظة تحميله إلى الذاكرة، وفى لحظة إلغاء تحميله ،أو فى لحظة تصدير المعالجات أو استلامها من DLL المستخدم من قبل عدة تطبيقات . لالتقاط هذه الرسائل وفحصها ووضع خدمات بناءً عليها فإنك يجب أن تبني إجراء بشكل خاص وتسند عنوان هذا الإجراء إلى المتغير العام DLLProc ،والشكل النموذجي للإجراء الذي يمكن أن يشير إليه المتغير DLLProc كالتالي :



procedure MyDLLProc(Reason: Integer);
begin
if Reason = DLL_PROCESS_DETACH then
{ DLL is unloading. Cleanup code here. }
end;







بعد كتابة الإجراء داخل DLL كأي إجراء آخر عليك أن تسند عنوان الإجراء إلى المتغير العام DLLProc وذلك فى جزء كتلة الكود الرئيسي لل DLL ،مثلاً :



begin
DLLProc := @MyDLLProc;
{ More initialization code. }
end.
هذا الكود سيستدعى أوتوماتيكيا فى الحالات الثلاث التى ذكرتها سابقاً ، الباراميتر Reason الذي هو المحدد الوحيد للإجراء( المؤشَّر عليه بالمتغير DLLProc ) قيمة هذا الباراميتر تحدد أي الحالات الثلاث حدثت ،وكما لاحظت فى الشكل النموذجى لإجراء DLLProc فإنه قد تم فحص الباراميتر Reason ومقارنته بقيمة معينة ، وإليك الحالات الثلاث لهذا الباراميتر:



-DLL_PROCESS_DETACH : وهى الرسالة التى يمكن أن تُستلم مرة واحدة ضمن حياة DLL وهى لحظة إلغاء تحميل DLL من الذاكرة (Unload )، مثل حالة استدعاء FreeLibrary (سنراها لاحقاً) .



-كلا من DLL_THREAD_ATTACH و DLL_ THREAD _DETACH : وهما تستلمان من قبل DLL عدة مرات خلال حياته (وجوده بالذاكرة) عندما يكون DLLL مُستخدماً من قبل عدة معالجات سواءً عدة تطبيقات أو تطبيق واحد يملك عدة مسالك لاستخدام DLL (عدة إجراءات مثلاً)،فالرسالة الأولى تشير إلى أن المعالجة الحالية قد بنت مسلكاً جديداً ،أما الرسالة الثانية فهى على عكسها تشير إلى أن المعالجة الحالية قد أنهت استعمالها لل DLL .



ملاحظة: طبقاً لما سبق قد تتساءل لماذا لا يتم فحص حالة بدء تحميل DLL وهى الرسالة DLL_PROCESS_ATTACH ؟ هذا فى الحقيقة لا يحدث والسبب هو أن Windows يعرف هذه الرسالة لكن Object Pascal لا تمررها إلى DLLProc ، بدلاً عن ذلك فإن Object Pascal تستدعى جزء الكود الرئيسي الموجود فى DLL عند لحظة استلامها للرسالة DLL_PROCESS_ATTACH من النظام Windows ،وهذا يغني عن تمرير هذه الرسالة إلى DLL ،حيث يتم وضع أي كود استهلالي فى جزء الكود الرئيسي لل DLL بين كلمتي Begin و End .



مثال على استخدام DLLProc :



library TestDLL;
uses
SysUtils,
Classes,
Forms,
Windows;
var
SomeBuffer : Pointer;
procedure MyDLLProc(Reason: Integer);
begin
if Reason = DLL_PROCESS_DETACH then
{ DLL is unloading. Cleanup code here. }
FreeMem(SomeBuffer);
end;
procedure SayHello(AForm : TForm);
begin
MessageBox(AForm.Handle, `Hello From a DLL!',
`DLL Message Box', MB_OK or MB_ICONEXCLAMATION);
end;
{ More DLL code here that uses SomeBuffer. }
exports
SayHello;
begin
{ Assign our DLLProc to the DLLProc global variable. }
DLLProc := @MyDLLProc;
SomeBuffer := AllocMem(1024);
end.

بإلقاء نظرة عامة على المثال السابق نلاحظ تعريف متغير عام SomeBuffer من النوع مؤشر واستخدم لحجز ذاكرة فى كتلة الكود الرئيسي (لا تنسى أن كتلة الكود الرئيسي تنفذ فى لحظة تحميل DLL ) يمكن لل DLL استخدام هذه الذاكرة لمعالجاته المختلفة ،والنقطة الهامة هى لحظة تحرير هذه الذاكرة كما سبق وعرفنا أن هذه الذاكرة تحرر ضمن الإجراء المؤشر عليه بالمتغير العام DLLProc فى مثالنا أسم الإجراء MyDLLProc الذى يشترط لتحريرها أن يستلم رسالة تفيد بأن ال DLL هو الآن قيد إلغاء التحميل (Unload )التى يحددها الباراميتر Reason عبر القيمة DLL_PROCESS_DETACH .



الخلاصة:



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


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


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


تقييم
0.00/10 (0 صوت)


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