لماذا استخدام الحزم؟
الجواب بسيط: بسبب قوة الحزمة. تعمل حزم وقت التصميم على تبسيط إصدار المكونات المخصصة وتثبيتها؛ وتضخ حزم وقت التشغيل طاقة جديدة إلى البرمجة التقليدية. بمجرد تجميع التعليمات البرمجية القابلة لإعادة الاستخدام في مكتبة وقت التشغيل، يمكنك مشاركتها عبر تطبيقات متعددة. يمكن لجميع التطبيقات الوصول إلى المكونات القياسية من خلال الحزم، وتقوم دلفي بنفسها بذلك. نظرًا لأن التطبيق لا يحتاج إلى نسخ مكتبة مكونات منفصلة في الملف القابل للتنفيذ، فإن هذا يوفر موارد النظام ومساحة القرص بشكل كبير. بالإضافة إلى ذلك، تعمل الحزم على تقليل الوقت المستغرق في الترجمة لأنك تحتاج فقط إلى ترجمة التعليمات البرمجية الخاصة بالتطبيق.
إذا كان من الممكن استخدام الحزم ديناميكيًا، فيمكننا الحصول على المزيد من الفوائد. توفر الحزم نهجا معياريا جديدا لتطوير التطبيقات. في بعض الأحيان قد ترغب في جعل وحدات معينة مكونات اختيارية للتطبيق، مثل نظام المحاسبة الذي يأتي مع وحدة الموارد البشرية الاختيارية. في بعض الحالات، تحتاج فقط إلى تثبيت التطبيق الأساسي، بينما في حالات أخرى قد تحتاج إلى تثبيت وحدات إضافية للموارد البشرية. يمكن تنفيذ هذا النهج المعياري بسهولة من خلال تقنية الحزمة. في الماضي، لم يكن من الممكن تحقيق ذلك إلا عن طريق التحميل الديناميكي لملف DLL، ولكن باستخدام تقنية التغليف الخاصة بـ Delphi، يمكنك "تجميع" كل نوع وحدة من التطبيق في حزم. على وجه الخصوص، كائنات الفئة التي تم إنشاؤها من الحزم مملوكة للتطبيق وبالتالي يمكنها التفاعل مع الكائنات الموجودة في التطبيق.
حزم وتطبيقات وقت التشغيل
العديد من المطورين يفكرون في حزم دلفي فقط كمكان لوضع المكونات، بينما في الواقع يمكن (ويجب) استخدام الحزم في تصميم التطبيقات المعيارية.
لتوضيح كيفية استخدام الحزم لنموذجية تطبيقك، فلنقم بإنشاء مثال:
1. قم بإنشاء برنامج دلفي جديد بنموذجين: Form1 وForm2؛
2. قم بإزالة Form2 من قائمة النماذج التي تم إنشاؤها تلقائيًا (PRoject | Options | Forms)؛
3. ضع زرًا في Form1 وأدخل الكود التالي في معالج أحداث OnClick الخاص بالزر:
مع TForm2.Create(التطبيق) القيام به
يبدأ
this.ShowModal;
حر؛
نهاية؛
4. تذكر إضافة الوحدة 2 إلى جملة الاستخدامات الخاصة بالوحدة 1؛
5. حفظ المشروع وتشغيله.
لقد أنشأنا تطبيقًا بسيطًا يعرض نموذجًا باستخدام زر، عند النقر عليه، يقوم بإنشاء نموذج آخر وعرضه.
ولكن ماذا يجب أن نفعل إذا أردنا تضمين Form2 في المثال أعلاه في وحدة قابلة لإعادة الاستخدام وجعلها تعمل بشكل طبيعي؟
الجواب هو: باو!
لإنشاء حزمة لـ Form2 يتطلب العمل التالي:
1. افتح مدير المشروع (عرض | مدير المشروع)؛
2. انقر بزر الماوس الأيمن فوق مجموعة المشروع وحدد "إضافة مشروع جديد..."؛
3. حدد "حزمة" في قائمة المشاريع "الجديدة"؛
4. الآن يجب أن تكون قادرًا على رؤية محرر الحزمة؛
5. حدد العنصر "يحتوي على" وانقر فوق الزر "إضافة"؛
6. ثم انقر فوق الزر "استعراض..." وحدد "Unit2.pas"؛
7. يجب أن تحتوي الحزمة الآن على وحدة "Unit2.pas"؛
8. أخيرًا قم بحفظ الحزمة وتجميعها.
الآن أكملنا الحزمة. يجب أن يكون هناك ملف باسم "package1.bpl" في دليل Project/BPL الخاص بك. (BPL هو اختصار لمكتبة حزمة Borland، وDCP هو اختصار لـ Delphi CompiledPackage.)
هذه الحزمة كاملة. الآن نحن بحاجة إلى تشغيل مفتاح خيارات الحزمة
وإعادة ترجمة التطبيق الأصلي.
1. انقر نقرًا مزدوجًا فوق "Project1.exe" في مدير المشروع لتحديد المشروع؛
2. انقر بزر الماوس الأيمن وحدد "خيارات..." (يمكنك أيضًا تحديد المشروع | خيارات... من القائمة)؛
3. حدد صفحة خيار "الحزم"؛
4. حدد خانة الاختيار "الإنشاء باستخدام حزم وقت التشغيل"؛
5. قم بتحرير مربع تحرير "حزم وقت التشغيل": "Vcl50;Package1" وانقر فوق الزر "موافق"؛
6. ملاحظة: لا تقم بإزالة Unit2 من التطبيق؛
7. قم بحفظ التطبيق وتشغيله.
سيتم تشغيل التطبيق كما كان من قبل، ولكن يمكن ملاحظة الفرق في حجم الملف.
يبلغ حجم Project1.exe الآن 14 كيلو بايت فقط، مقارنة بـ 293 كيلو بايت سابقًا. إذا كنت تستخدم مستعرض الموارد لعرض محتويات ملفات EXE وBPL، فسترى أنه تم الآن حفظ Form2 DFM والتعليمات البرمجية في الحزمة.
تكمل دلفي الارتباط الثابت للحزم أثناء التجميع. (ولهذا السبب لا يمكنك إزالة Unit2 من مشروع EXE.)
فكر في ما يمكنك الحصول عليه من هذا: يمكنك إنشاء وحدة نمطية للوصول إلى البيانات في الحزمة، وعندما تقوم بتغيير قواعد الوصول إلى البيانات (مثل التبديل من اتصالات BDE إلى اتصالات ADO)، قم بتعديل الحزمة قليلاً وإعادة نشرها. وبدلاً من ذلك، يمكنك إنشاء نموذج في حزمة واحدة يعرض الرسالة "هذا الخيار غير متوفر في الإصدار الحالي" ثم إنشاء نموذج كامل الوظائف في حزمة أخرى بنفس الاسم. الآن لدينا المنتج في الإصدارين "Pro" و"Enterprise" دون أي جهد.
التحميل والتفريغ الديناميكي للحزم
في معظم الحالات، سيكون DLL أو BPL المرتبط بشكل ثابت كافيًا. ولكن ماذا لو كنا لا نريد إطلاق BPL؟ "لا يمكن العثور على مكتبة الارتباط الديناميكي Package1.bpl في الدليل المحدد" هي الرسالة الوحيدة التي يمكننا الحصول عليها قبل إنهاء التطبيق. أو، في تطبيق معياري، هل يمكننا استخدام أي عدد من المكونات الإضافية؟
نحن بحاجة إلى الاتصال ديناميكيًا بـ BPL في وقت التشغيل.
بالنسبة لمكتبات DLL، هناك طريقة بسيطة وهي استخدام وظيفة LoadLibrary:
وظيفة LoadLibrary(lpLibFileName: Pchar): HMODULE;stdcall;
بعد تحميل ملف DLL، يمكننا استخدام الدالة GetProcAddress لاستدعاء وظائف وأساليب DLL المصدرة:
وظيفة GetProcAddress(hModule: HMODULE; lpProcName:LPCSTR): FARPROC;
وأخيرًا، نستخدم FreeLibrary لإلغاء تثبيت DLL:
وظيفة FreeLibrary(hLibModule: HMODULE): BOOL;stdcall;
في المثال التالي، نقوم بتحميل مكتبة HtmlHelp الخاصة بشركة Microsoft ديناميكيًا:
وظيفة TForm1.ApplicationEvents1Help(Command: Word; Data: Integer; var CallHelp: Boolean):Boolean;
يكتب
TFNHtmlHelpA = function(hwndCaller: HWND; pszFile: PansiChar; uCommand: UINT;dwData: Dword): HWND;
فار
HelpModule: Hmodule;
HtmlHelp: TFNHtmlHelpA;
يبدأ
النتيجة:= خطأ؛
HelpModule := LoadLibrary('HHCTRL.OCX');
إذا HelpModule <> 0 ثم
يبدأ
@HtmlHelp := GetProcAddress(HelpModule, 'HtmlHelpA');
إذا @HtmlHelp <> لا شيء إذن
النتيجة := HtmlHelp(Application.Handle,Pchar(Application.HelpFile), Command,Data) <> 0;
FreeLibrary(HelpModule);
نهاية؛
CallHelp := خطأ؛
نهاية؛
تحميل BPL ديناميكيًا
يمكننا استخدام نفس الطريقة البسيطة للتعامل مع BPL، أو ينبغي أن أقول نفس الطريقة البسيطة بشكل أساسي.
يمكننا تحميل الحزم ديناميكيًا باستخدام وظيفة LoadPackage:
وظيفة LoadPackage (اسم ثابت: سلسلة): HMODULE؛
ثم استخدم الدالة GetClass لإنشاء كائن نوع TPersistentClass:
دالة GetClass(const AclassName: string):TPersistentClass;
بعد الانتهاء من كل شيء، استخدم UnLoadPackage(Module:HModule);
دعونا نجري بعض التغييرات الصغيرة على الكود الأصلي:
1. حدد "Project1.exe" في مدير المشروع؛
2. انقر بزر الماوس الأيمن عليه وحدد "خيارات...";
3. حدد صفحة خيار "الحزم"؛
4. قم بإزالة "Package1" من مربع التحرير "حزم وقت التشغيل" وانقر فوق الزر "موافق"؛
5. في شريط أدوات دلفي، انقر فوق الزر "إزالة الملف من المشروع"؛
6. حدد "Unit2 | Form2" ثم انقر فوق "موافق"؛
7. الآن في الكود المصدري لـ "Unit1.pas"، قم بإزالة Unit2 من جملة الاستخدامات؛
8. أدخل رمز وقت OnClick الخاص بـ Button1؛
9. أضف متغيرين من النوع HModule وTPersistentClass:
فار
PackageModule: HModule;
الفئة: TPersistentClass؛
10. استخدم وظيفة LoadPackage لتحميل حزمة Pacakge1:
PackageModule := LoadPackage('Package1.bpl');
11. تحقق مما إذا كانت PackageModule هي 0؛
12. استخدم وظيفة GetClass لإنشاء نوع ثابت:
AClass := GetClass('TForm2');
13. إذا لم يكن هذا النوع الثابت صفراً، فيمكننا العودة إلى السابق
قم بإنشاء واستخدام كائنات من هذا النوع بنفس الطريقة:
باستخدام TComponentClass(AClass).إنشاء(Application) كما يفعل TcustomForm
يبدأ
this.ShowModal;
حر؛
نهاية؛
14. وأخيرًا، استخدم عملية UnloadPackage لإلغاء تثبيت الحزمة:
UnloadPackage(PackageModule);
15. احفظ المشروع.
فيما يلي القائمة الكاملة لمعالجات أحداث OnClick:
الإجراء TForm1.Button1Click(Sender: Tobject);
فار
PackageModule: HModule;
الفئة: TPersistentClass؛
يبدأ
PackageModule := LoadPackage('Package1.bpl');
إذا كان PackageModule <> 0 ثم
يبدأ
AClass := GetClass('TForm2');
إذا AClass <> لا شيء ثم
باستخدام TComponentClass(AClass).إنشاء(Application) كما يفعل TcustomForm
يبدأ
this.ShowModal;
حر؛
نهاية؛
UnloadPackage(PackageModule);
نهاية؛
نهاية؛
لسوء الحظ، هذا ليس كل شيء.
المشكلة هي أن وظيفة GetClass يمكنها فقط البحث عن الأنواع المسجلة. يتم تسجيل فئات النماذج وفئات المكونات التي يتم الرجوع إليها عادةً في النموذج تلقائيًا عند تحميل النموذج. لكن في حالتنا، لا يمكن تحميل النموذج مبكرًا. فأين نسجل النوع؟ الجواب هو في الحقيبة. تتم تهيئة كل وحدة في الحزمة عند تحميل الحزمة وتنظيفها عند تفريغ الحزمة.
والآن نعود إلى مثالنا:
1. انقر نقرًا مزدوجًا فوق "Package1.bpl" في مدير المشروع؛
2. انقر فوق علامة + بجوار "Unit2" في قسم "يحتوي على"؛
3. انقر نقرًا مزدوجًا فوق "Unit2.pas" لتنشيط محرر الكود المصدري للوحدة؛
4. أضف قسم التهيئة في نهاية الملف؛
5. استخدم إجراء RegisterClass لتسجيل نوع النموذج:
RegisterClass(TForm2);
6. أضف قسم الإنهاء.
7. استخدم الإجراء UnRegisterClass لإلغاء تسجيل نوع النموذج:
UnRegisterClass(TForm2);
8. أخيرًا، احفظ الحزمة وقم بتجميعها.
الآن يمكننا تشغيل "Project1" بأمان وسيعمل تمامًا كما كان من قبل، ولكن الآن يمكنك تحميل الحزم كما تريد.
نهاية
تذكر، سواء كنت تستخدم الحزم بشكل ثابت أو ديناميكي، قم بتشغيل Project |. الحزم |.
قبل أن تقوم بإلغاء تثبيت حزمة، تذكر تدمير كافة كائنات الفئة الموجودة في الحزمة وإلغاء تسجيل كافة الفئات المسجلة. قد تساعدك العملية التالية:
الإجراء DoUnloadPackage(Module: HModule);
فار
ط: عدد صحيح؛
M: TMemoryBasicInformation;
يبدأ
لأني := Application.ComponentCount - 1 وصولاً إلى 0 do
يبدأ
VirtualQuery(GetClass(Application.Components[i].ClassName), M, Sizeof(M));
إذا (Module = 0) أو (HMODULE(M.AlllocationBase) = Module) إذن
Application.Components[i].Free;
نهاية؛
UnregisterModuleClasses(Module);
UnloadPackage(Module);
نهاية؛
قبل تحميل الحزمة، يحتاج التطبيق إلى معرفة أسماء جميع الفئات المسجلة. إحدى طرق تحسين هذا الوضع هي إنشاء آلية تسجيل تخبر التطبيق بأسماء جميع الفئات المسجلة بواسطة الحزمة.
مثال
الحزم المتعددة: الحزم لا تدعم المراجع الدائرية. أي أن الوحدة لا يمكن أن تشير إلى وحدة تشير بالفعل إلى تلك الوحدة (هيهي). وهذا يجعل من الصعب تعيين قيم معينة في نموذج الاستدعاء بواسطة الطريقة المستدعىة.
الحل لهذه المشكلة هو إنشاء بعض الحزم الإضافية التي يتم الرجوع إليها بواسطة كل من كائن الاستدعاء والكائنات الموجودة في الحزمة. تخيل كيف نجعل التطبيق صاحب كل أشكاله؟ يتم إنشاء التطبيق المتغير في Forms.pas ويتم تضمينه في الحزمة VCL50.bpl. ربما لاحظت أن تطبيقك لا يحتاج فقط إلى ترجمة VCL50.pas، ولكنه يتطلب أيضًا VCL50 في الحزمة الخاصة بك.
في مثالنا الثالث، قمنا بتصميم تطبيق لعرض معلومات العملاء، وعند الطلب، طلبات العملاء (ديناميكيًا).
إذن أين يمكننا أن نبدأ؟ مثل جميع تطبيقات قواعد البيانات
الإجراء هو نفسه، نحن بحاجة إلى الاتصال. نقوم بإنشاء وحدة بيانات رئيسية تحتوي على اتصال TDataBase. ثم نقوم بتغليف وحدة البيانات هذه في حزمة (cst_main).
الآن في التطبيق، نقوم بإنشاء نموذج عميل ومرجع DataModuleMain (نقوم بربط VCL50 وcst_main بشكل ثابت).
ثم نقوم بإنشاء حزمة جديدة (cst_ordr) تحتوي على نموذج طلب العميل وتتطلب cst_main. يمكننا الآن تحميل cst_ordr ديناميكيًا في التطبيق. نظرًا لأن وحدة البيانات الرئيسية موجودة بالفعل قبل تحميل الحزمة الديناميكية، فيمكن لـ cst_ordr استخدام مثيل وحدة البيانات الرئيسية للتطبيق مباشرة.
الصورة أعلاه هي رسم تخطيطي وظيفي لهذا التطبيق:
الحزم القابلة للاستبدال: حالة استخدام أخرى للحزم هي إنشاء حزم قابلة للاستبدال. لا يتطلب تنفيذ هذه الوظيفة إمكانات التحميل الديناميكية للحزمة. لنفترض أننا نريد إصدار نسخة تجريبية محدودة المدة من البرنامج، فكيف نحقق ذلك؟
أولاً نقوم بإنشاء نموذج "Splash"، وهو عادةً صورة تحتوي على كلمة "Trial"، ونعرضه أثناء بدء تشغيل التطبيق. ثم نقوم بإنشاء نموذج "حول" الذي يوفر بعض المعلومات حول التطبيق. وأخيرًا، نقوم بإنشاء وظيفة تختبر ما إذا كان البرنامج قديمًا. نقوم بتغليف هذين النموذجين وهذه الوظيفة في حزمة وإصدارها مع الإصدار التجريبي من البرنامج.
بالنسبة للإصدار المدفوع، نقوم أيضًا بإنشاء نموذج "Splash" ونموذج "About" - بنفس أسماء الفئات مثل النموذجين السابقين - ووظيفة اختبار (لا تفعل شيئًا) ونضيفها مغلفة في حزمة بنفس اسم.
ماذا ماذا؟ هل هذا مفيد، تسأل؟ حسنًا، يمكننا إصدار نسخة تجريبية من البرنامج للجمهور. إذا قام أحد العملاء بشراء التطبيق، فنحن نحتاج فقط إلى إرسال الحزمة غير التجريبية. يؤدي هذا إلى تبسيط عملية إصدار البرنامج إلى حد كبير، حيث لا يلزم سوى تثبيت واحد وترقية حزمة تسجيل واحدة.
تفتح الحزمة بابًا آخر للتصميم المعياري لمجتمعات تطوير Delphi وC++ Builder. مع الحزم، لم تعد بحاجة إلى تمرير مقابض النوافذ، ولا مزيد من وظائف رد الاتصال، ولا مزيد من تقنيات DLL الأخرى. يؤدي هذا أيضًا إلى تقصير دورة تطوير البرمجة المعيارية. كل ما علينا فعله هو أن ندع حزم دلفي تعمل لصالحنا.