زنگ

کسانی هستند که قبل از شما این خبر را می خوانند.
برای دریافت آخرین مقالات مشترک شوید.
پست الکترونیک
نام
نام خانوادگی
چگونه می خواهید زنگ را بخوانید
بدون هرزنامه

همانطور که احتمالاً می دانید، کتابخانه های پیوند پویا (DLL) از قراردادهای زبان C هنگام اعلان اشیاء صادر شده استفاده می کنند، در حالی که C ++ از یک سیستم تولید نام کامپایل کمی متفاوت استفاده می کند، بنابراین شما نمی توانید فقط توابع را صادر کنید - متدهای کلاس C ++ و سپس از آنها در کد برنامه کلاینت استفاده کنید (از این پس کلاینت به معنای برنامه ای است که از DLL استفاده می کند). با این حال، این را می توان با استفاده از رابط های موجود برای DLL و برنامه مشتری انجام داد. این روش بسیار قدرتمند و در عین حال ظریف است. مشتری فقط رابط انتزاعی را می بیند و کلاس واقعی که همه توابع را پیاده سازی می کند می تواند هر چیزی باشد. فناوری COM مایکروسافت (Component مدل شیء) بر اساس یک ایده مشابه ساخته شده است (البته به علاوه عملکرد اضافی). این مقاله نحوه استفاده از رویکرد "کلاس" با رابط کاربری COM را برای اتصال زود هنگام (زمان کامپایل) و دیر (زمان اجرا) نشان می دهد.

اگر تا به حال با یک DLL کار کرده اید، از قبل می دانید که یک DLL دارای یک تابع خاص DllMain () است. این تابع شبیه WinMain یا main () است به این معنا که نوعی نقطه ورودی DLL است. اگر DLL بارگیری و تخلیه شود، سیستم عامل به طور خودکار این تابع را فراخوانی می کند. معمولا این تابع برای هیچ چیز دیگری استفاده نمی شود.

دو روش برای پیوند یک DLL به یک پروژه وجود دارد - پیوند زودهنگام (در زمان کامپایل) و دیر (در زمان اجرا). روش ها در نحوه بارگذاری DLL و روشی که در آن توابع پیاده سازی و صادر شده از DLL فراخوانی می شوند، متفاوت هستند.

پیوند اولیه (در زمان کامپایل)

با این روش پیوند، سیستم عامل به طور خودکار DLL را در هنگام راه اندازی برنامه بارگیری می کند. با این حال ، لازم است پروژه در حال توسعه شامل یک فایل .lib (فایل کتابخانه) مربوط به این DLL باشد. این فایل تمام اشیاء DLL صادر شده را تعریف می کند. اعلان ها می توانند شامل توابع یا کلاس های C منظم باشند. تمام کاری که کلاینت باید انجام دهد این است که از این فایل .lib استفاده کند و فایل هدر DLL را در آن قرار دهد - و سیستم عامل به طور خودکار این DLL را بارگیری می کند. همانطور که می بینید، استفاده از این روش بسیار آسان به نظر می رسد، زیرا همه چیز شفاف است با این حال، باید توجه داشته باشید که هر زمان که کد DLL تغییر می کند و یک فایل .lib جدید تولید می شود، باید کد کلاینت دوباره کامپایل شود. این که آیا برای برنامه شما مناسب است یا خیر به شما بستگی دارد. یک DLL می تواند توابعی را که می خواهد صادر کند با دو روش اعلام کند. روش استاندارد استفاده از فایل های .def است. این فایل .def فقط فهرستی از توابع صادر شده از DLL است.

// ================================================ ============ // .def فایل کتابخانه myfirstdll.dll توصیف "اولین DLL من" MyFunction را صادر می کند // ================== ==========================================// هدر DLL که شامل خواهد شد در کد مشتری bool MyFunction (int parms)؛ // ================================================ ============ // پیاده سازی تابع در DLL bool MyFunction (int parms) (// انجام هر کاری که لازم است ............)

من فکر می کنم نمی توان این را در این مورد گفت این مثالفقط یک MyFunction صادر می شود. روش دوم برای اعلام اشیاء صادر شده خاص است، اما بسیار قدرتمندتر: شما می توانید نه تنها توابع، بلکه کلاس ها و متغیرها را نیز صادر کنید. بیایید نگاهی به قطعه کد تولید شده در هنگام ایجاد VisualC ++ DLL AppWizard بیندازیم. نظرات موجود در لیست برای درک نحوه عملکرد آن کاملاً کافی است.

// ================================================ ============= // هدر DLL در کد مشتری گنجانده می شود / * بلوک ifdef بعدی - روش استانداردایجاد یک ماکرو که صادرات DLL را آسان تر می کند. تمام فایل های این DLL با یک کلید خاص MYFIRSTDLL_EXPORTS کامپایل می شوند. این کلید برای هیچ یک از پروژه هایی که از این DLL استفاده می کنند تعریف نشده است. بنابراین، هر پروژه ای که این فایل در آن گنجانده شده است، توابع MYFIRSTDLL_API را به عنوان وارد شده از یک DLL می بیند، در حالی که خود DLL همین توابع را به عنوان صادر شده می بیند. * / #ifdef MYFIRSTDLL_EXPORTS #define MYFIRSTDLL_API __declspec (dllexport) #else #define MYFIRSTDLL_API __declspec (dllimport) #endif // کلاس از test2.dll کلاس MYFIRSTDLL_F صادر می شود. خارجی MYFIRSTDLL_API int nMyFirstDll؛ MYFIRSTDLL_API int fnMyFunction (void);

در طول کامپایل DLL، کلید MYFIRSTDLL_EXPORTS تعریف می شود، بنابراین کلمه کلیدی __declspec (dllexport) قبل از اعلان اشیاء صادر شده جایگزین می شود. و هنگامی که کد کلاینت کامپایل می شود، این کلید تعریف نشده است و پیشوند اشیاء با __declspec (dllimport) قرار می گیرد تا مشتری بداند کدام اشیاء از DLL وارد می شوند.

در هر دو مورد ، تنها کاری که مشتری باید انجام دهد این است که فایل myfirstdll.lib را به پروژه اضافه کند و شامل یک فایل سربرگ باشد که اشیاء وارد شده از DLL را اعلام می کند و سپس از این اشیاء (توابع ، کلاسها و متغیرها) دقیقاً مانند آنها استفاده کند. به صورت محلی در پروژه تعریف و اجرا شدند. حال بیایید به روش دیگری برای استفاده از DLL ها نگاه کنیم که اغلب راحت تر و قدرتمندتر است.

اتصال دیرهنگام (زمانی که برنامه در حال اجراست)

هنگامی که از پیوند دیرهنگام استفاده می شود ، DLL هنگام شروع برنامه به طور خودکار بارگیری نمی شود ، بلکه مستقیماً در کدی که در آن نیاز است بارگذاری می شود. نیازی به استفاده از فایل های .lib نیست ، بنابراین برنامه کلاینت نیازی به کامپایل مجدد هنگام تغییر DLL ندارد. این پیوند دقیقاً به این دلیل قدرتمند است که شما تصمیم می‌گیرید چه زمانی و کدام DLL را بارگیری کنید. به عنوان مثال، شما در حال نوشتن یک بازی هستید که از DirectX و OpenGL استفاده می کند. شما می توانید به سادگی تمام کدهای لازم را در فایل اجرایی قرار دهید، اما در این صورت تشخیص چیزی غیرممکن خواهد بود. یا می توانید کدهای DirectX را در یک DLL و کد OpenGL را در دیگری قرار دهید و آنها را به صورت ایستا به پروژه خود پیوند دهید. اما اکنون همه کدها به یکدیگر وابسته هستند، بنابراین اگر یک DLL جدید حاوی کد DirectX بنویسید، باید فایل اجرایی را نیز دوباره کامپایل کنید. تنها راحتی این است که نیازی به نگرانی در مورد بارگذاری ندارید (اگرچه معلوم نیست که هر دو DLL را بارگیری کرده و حافظه را اشغال کنید ، و آیا در واقع فقط به یکی از آنها احتیاج دارید یا خیر. و در نهایت به نظر من بهترین ایدهاین است که اجازه دهید دستگاه اجرایی تصمیم بگیرد که کدام DLL در هنگام بارگیری بارگیری شود. به عنوان مثال، اگر برنامه تشخیص دهد که سیستم از شتاب OpenGL پشتیبانی نمی کند، بهتر است DLL را با کد DirectX بارگذاری کنید، در غیر این صورت OpenGL را بارگذاری کنید. بنابراین، اتصال دیرهنگام باعث صرفه جویی در حافظه و کاهش وابستگی بین DLL و فایل اجرایی می شود. با این حال، در این مورد، محدودیتی برای اشیاء صادر شده اعمال می شود - فقط توابع سبک C را می توان صادر کرد. اگر برنامه از اتصال دیرهنگام استفاده می کند، کلاس ها و متغیرها نمی توانند بارگیری شوند. بیایید ببینیم چگونه می توان با استفاده از رابط ها این محدودیت را دور زد.

یک DLL که برای پیوند دیرهنگام طراحی شده است، معمولاً از یک فایل .def برای تعیین اینکه کدام اشیاء را می‌خواهد صادر کند، استفاده می‌کند. اگر نمی‌خواهید از فایل .def استفاده کنید، می‌توانید به سادگی از پیشوند __declspec (dllexport) در جلوی توابع صادر شده استفاده کنید. هر دو روش یک کار را انجام می دهند. کلاینت DLL را با ارسال نام فایل DLL به تابع Win32 LoadLibrary () بارگیری می کند. این تابع دسته HINSTANCE را برمی گرداند که برای کار با DLL استفاده می شود و برای تخلیه DLL از حافظه در زمانی که وجود ندارد لازم است. دیگر مورد نیاز است. پس از بارگیری DLL، مشتری می تواند با استفاده از تابع GetProcAddress () اشاره گر به هر تابعی را با استفاده از نام تابع مورد نیاز به عنوان پارامتر دریافت کند.

// ================================================ ============= // فایل .def LIBRARY myfirstdll.dll توضیحات "اولین DLL من" عملکرد من را صادر می کند // ================== = ======================================== * * اجرای تابع در DLL * / bool MyFunction (int parms) (// کاری انجام دهید ............) // ========================= ====================================// کد مشتری / * اعلان تابع واقعاً فقط برای به منظور تعریف پارامترها اعلان های عملکرد معمولاً در یک فایل سربرگ همراه با DLL موجود است. کلمه کلیدی خارجی C در اعلان تابع به کامپایلر می گوید که از قراردادهای نامگذاری C * / extern "C" bool MyFunction (int parms) استفاده کند. typedef bool (* MYFUNCTION) (int parms); MYFUNCTION pfnMyFunc = 0; // اشاره گر به MyFunction HINSTANCE hMyDll = :: LoadLibrary ("myfirstdll.dll"); if (hMyDll! = NULL) (// آدرس تابع pfnMyFunc = (MYFUNCTION) را تعیین کنید :: GetProcAddress (hMyDll، "MyFunction")؛ // اگر ناموفق بود، DLL را بارگیری کنید اگر (pfnMyFunc == 0) (: FreeLibrary (hMyDll) ; return;) // فراخوانی تابع bool result = pfnMyFunc (parms)؛ // اگر دیگر به آن نیاز نداریم DLL را بارگیری کنیم :: FreeLibrary (hMyDll)؛)

همانطور که می بینید، کد بسیار ساده است. حال بیایید ببینیم چگونه می توان کار با "کلاس ها" را پیاده سازی کرد. همانطور که قبلاً گفته شد ، اگر از اتصال دیرهنگام استفاده شود ، راهی مستقیم برای وارد کردن کلاس ها از DLL وجود ندارد ، بنابراین ما باید "عملکرد" ​​کلاس را با استفاده از رابط کاربری که شامل همه توابع عمومی به جز سازنده و تخریب کننده است پیاده سازی کنیم. این رابط یک ساختار C / C ++ معمولی خواهد بود که فقط شامل توابع عضو انتزاعی مجازی است. کلاس واقعی در DLL از این ساختار به ارث می رسد و تمام عملکردهای تعریف شده در رابط را پیاده سازی می کند. حال، برای دسترسی به این کلاس از برنامه کلاینت، تنها کاری که باید انجام دهید این است که توابع C-style مربوط به نمونه کلاس را صادر کرده و آنها را با رابطی که تعریف کردیم مرتبط کنید تا مشتری بتواند از آنها استفاده کند. برای پیاده سازی چنین روشی ، دو عملکرد دیگر مورد نیاز است که یکی از آنها رابط را ایجاد می کند ، و دومی پس از اتمام کار با آن ، رابط را حذف می کند. نمونه ای از اجرای این ایده در زیر آورده شده است.

// ============================================= ============ // فایل .def LIBRARY myinterface.dll توضیحات "رابط I_MyInterface EXPORTS GetMyInterface FreeMyInterface // ================== = ======================================= // فایل هدر مورد استفاده در Dll و کلاینت، // که رابط را اعلام می کند // I_MyInterface.h ساختار I_MyInterface (نقل مجازی Init (int parms) = 0؛ نسخه مجازی نسخه () = 0؛ مجازی void DoStuff () = 0;)؛ / * اعلامیه های توابع صادر شده تعاریف dll و نوع برای اشاره گرهای تابع برای بارگیری و دستکاری آسان توابع به پیشوند خارجی "C" توجه کنید، که به کامپایلر می گوید از توابع به سبک C * / خارجی "C" استفاده کند (HRESULT GetMyInterface (I_MyInterface ** pInterface)؛ typedef HRESULT (* GETINTERFACE) (I_MyInterface ** pInterface)؛ HRESULT FreeMyInterface (I_MyInterface ** pInterface)؛ typedef HRESULT (* FREEINTERFACE) (I_MyInterface ** pInterface);) =/ ======== =============================== ============= // پیاده سازی رابط در Dll // MyInterface.h کلاس CMyClass: public I_MyInterface (public: bool Init (int parms) ؛ bool Release (); void DoStuff ()؛ CMyClass (); ~ CMyClass (); // سایر اعضای کلاس ............ خصوصی: // اعضای کلاس ............); // ============================================= ============ // توابع صادر شده که یک رابط ایجاد و تخریب می کنند // Dllmain.h HRESULT GetMyInterface (I_MyInterface ** pInterface) (اگر (! * PInterface) (* pInterface = CMyClass جدید؛ بازگشت S_OK;) بازگشت E_FAIL;) HRESULT FreeMyInterface (I_MyInterface ** pInterface) (اگر (! * PInterface) بازگشت E_FAIL؛ حذف * pInterface؛ * pInterface = 0؛ برگرداندن S_OK؛) // ========== ================================================= // کد کلاینت // اعلان‌های رابط و فراخوانی تابع GETINTERFACE pfnInterface = 0؛ // اشاره‌گر به تابع GetMyInterface I_MyInterface * pInterface = 0؛ // اشاره‌گر برای ساختار MyInterface HINSTANCE hMyDll = ::" Loadmyin"face; if (hMyDll! = NULL) (// آدرس تابع را تعیین کنید pfnInterface = (GETINTERFACE) :: GetProcAddress (hMyDll ، "GetMyInterface") ؛ // اگر عملیات قبلی اگر (pfnInterface == 0) انجام نشد DLL را تخلیه کنید ( :: FreeLibrary (hMyDll)؛ return;) // فراخوانی تابع HRESULT hr = pfnInterface (& pInterface)؛ // در صورت ناموفق بودن بارگیری (FILED (hr)) (:: FreeLibrary (hMyDll); return;) // رابط بارگذاری شده است، می توانید توابع pInterface-> Init (1)؛ pInterface-> DoStuff ()؛ pInterface-> Release ()؛ // رابط را آزاد کنید. اگر (pfnFree! = 0) pfnFree (& hMyDll)؛ // Unload DLL :: FreeLibrary (hMyDll); )

این اطلاعات کافی است تا احساس راحتی در استفاده از رابط ها داشته باشید. برنامه نویسی مبارک!

از بدو تولد (یا کمی بعد)، سیستم عامل ویندوز از DLL ها (کتابخانه پیوند پویا) استفاده می کرد که شامل پیاده سازی هایی از متداول ترین توابع استفاده می شد. وارثان ویندوز - NT و Windows 95، و همچنین OS / 2 - نیز برای ارائه بسیاری از عملکردهای خود به DLL ها وابسته هستند.

بیایید به تعدادی از جنبه های ایجاد و استفاده از DLL نگاه کنیم:

  • نحوه پیوند ثابت DLL ها
  • نحوه بارگذاری DLL به صورت پویا
  • نحوه ایجاد DLL;
  • نحوه ایجاد پسوند MFC DLL.

استفاده از DLL

ایجاد تقریبا غیرممکن است اپلیکیشن ویندوزکه از DLL استفاده نمی کند. یک DLL شامل تمام توابع Win32 API و توابع بی شمار دیگر سیستم عامل های Win32 است.

به طور کلی، DLL ها به سادگی مجموعه ای از توابع هستند که در کتابخانه ها مونتاژ شده اند. با این حال، بر خلاف پسرعموهای استاتیک خود (فایل‌های lib.)، DLL‌ها مستقیماً با استفاده از پیوند دهنده به فایل‌های اجرایی مرتبط نمی‌شوند. فایل اجرایی فقط حاوی اطلاعات مربوط به مکان آنهاست. در زمان اجرای برنامه ، کل کتابخانه بارگیری می شود. این به فرآیندهای مختلف اجازه می دهد تا کتابخانه های درون حافظه مشابهی را به اشتراک بگذارند. این رویکرد به شما امکان می دهد مقدار حافظه مورد نیاز برای برنامه های متعددی که از کتابخانه های مشترک زیادی استفاده می کنند را کاهش دهید و همچنین اندازه فایل های EXE را کنترل کنید.

با این حال، اگر کتابخانه تنها توسط یک برنامه استفاده می شود، بهتر است آن را به یک کتابخانه معمولی و ثابت تبدیل کنید. البته اگر توابع موجود در آن تنها در یک برنامه مورد استفاده قرار گیرد، می توانید به سادگی فایل منبع مربوطه را در آن قرار دهید.

اغلب، یک پروژه در زمان پیوند به صورت ایستا یا ضمنی به یک DLL مرتبط می شود. سیستم عامل بارگذاری DLL را در حین اجرای برنامه کنترل می کند. با این حال، یک DLL می تواند به صورت صریح یا پویا در طول یک برنامه بارگذاری شود.

وارد کردن کتابخانه ها

هنگامی که یک DLL به صورت ایستا پیوند داده می شود ، نام فایل .lib در میان سایر پارامترهای پیوند دهنده در خط فرمانیا در برگه پیوند از کادر گفتگوی تنظیمات پروژه در Developer Studio. با این حال .lib فایل مورد استفاده توسط هنگام پیوند ضمنی یک DLL،یک کتابخانه ثابت معمولی نیست. چنین فایل های lib. نامیده می شوند واردات کتابخانه ها(واردات کتابخانه ها). آنها شامل خود کد کتابخانه نیستند، بلکه فقط به همه توابع صادر شده از فایل DLL اشاره می کنند که همه چیز در آن ذخیره می شود. در نتیجه، کتابخانه‌های وارداتی کوچک‌تر از فایل‌های DLL هستند. ما بعداً به روش های ایجاد آنها باز خواهیم گشت. حال بیایید به مسائل دیگر مربوط به پیوند ضمنی کتابخانه های پویا نگاه کنیم.

مذاکره رابط

هنگام استفاده از کتابخانه های خود یا شخص ثالث ، باید به تطبیق فراخوانی تابع با نمونه اولیه آن توجه کنید.

اگر دنیا عالی بود، برنامه نویسان مجبور نبودند نگران تطبیق رابط های عملکردی هنگام پیوند دادن کتابخانه ها باشند - همه آنها یکسان بودند. با این حال ، جهان از ایده آل بسیار دور است و بسیاری از آنها برنامه های بزرگبا استفاده از کتابخانه های مختلف بدون C ++ نوشته می شوند.

به طور پیش فرض ، در Visual C ++ ، رابط های عملکردی مطابق قوانین C ++ توافق می کنند. این بدان معنی است که پارامترها از راست به چپ به پشته فشار داده می شوند، برنامه فراخوانی مسئول حذف آنها از پشته هنگام خروج تابع و گسترش نام خود است. انحراف نام به پیوند دهنده اجازه می دهد تا بین توابع بیش از حد بارگذاری شده تمایز قائل شود. توابع با نام یکسان اما لیست های آرگومان متفاوت. با این حال، در کتابخانه قدیمی C، هیچ توابعی با نام توسعه یافته وجود ندارد.

اگرچه همه قوانین دیگر برای فراخوانی یک تابع در C مشابه قوانین فراخوانی یک تابع در C ++ است ، اما نام تابع در کتابخانه های C گسترش نمی یابد. آنها فقط با یک خط زیر (_) در جلو اضافه می شوند.

اگر نیاز به اتصال یک کتابخانه C به یک برنامه C ++ دارید، تمام توابع این کتابخانه باید به عنوان خارجی در قالب C اعلام شوند:

خارجی "С" int MyOldCFunction (int myParam);

اعلان‌های تابع کتابخانه معمولاً در فایل هدر آن کتابخانه قرار می‌گیرند، اگرچه سرصفحه‌های اکثر کتابخانه‌های C برای استفاده در پروژه‌های C ++ در نظر گرفته نشده است. در این مورد، شما باید یک کپی از فایل هدر ایجاد کنید و اصلاح کننده خارجی "C" را در آن به اعلان تمام توابع کتابخانه استفاده شده اضافه کنید. اصلاح کننده خارجی "C" همچنین می تواند برای کل بلوک اعمال شود، که فایل هدر C قدیمی با استفاده از دستور #tinclude به آن متصل شده است. بنابراین، به جای اینکه هر تابع را به طور جداگانه تغییر دهید، فقط با سه خط می توانید از پس آن برآیید:

خارجی "C" (#شامل "MyCLib.h")

برنامه‌های نسخه‌های قدیمی‌تر ویندوز نیز از قراردادهای فراخوانی تابع استفاده می‌کردند زبان پاسکالبرای توابع Windows API... برنامه های جدیدتر باید از اصلاح کننده winapi استفاده کنند که به _stdcall تبدیل شده است. اگرچه یک رابط تابع استاندارد C یا C ++ نیست، اما برای فراخوانی توابع API ویندوز استفاده می شود. با این حال، معمولاً همه اینها قبلاً در هدرهای استاندارد ویندوز در نظر گرفته شده است.

بارگیری یک DLL پیوند ضمنی

در هنگام راه اندازی، برنامه سعی می کند تمام فایل های DLL را که به طور ضمنی به برنامه متصل هستند، پیدا کند و آنها را در ناحیه حافظه اشغال شده توسط این فرآیند قرار دهد. سیستم عامل فایل های DLL را به ترتیب زیر جستجو می کند.

  • دایرکتوری که فایل EXE در آن قرار دارد.
  • دایرکتوری فعلی فرآیند.
  • دایرکتوری سیستم ویندوز

اگر DLL پیدا نشد، برنامه یک کادر محاوره ای با پیامی در مورد عدم وجود آن و مسیرهایی که جستجو شده را نمایش می دهد. سپس فرآیند خاموش می شود.

اگر کتابخانه مورد نیاز پیدا شود، در حافظه رم فرآیند قرار می گیرد و تا پایان آن در آنجا باقی می ماند. برنامه اکنون می تواند به توابع موجود در DLL دسترسی داشته باشد.

بارگیری و تخلیه DLL پویا

به جای اینکه زمانی که برنامه برای اولین بار در RAM بارگذاری می شود، ویندوز به صورت پویا به DLL پیوند داده شود، می توانید برنامه را در زمان اجرا به ماژول کتابخانه پیوند دهید (به این ترتیب، نیازی به استفاده از کتابخانه import در طول فرآیند ایجاد برنامه ندارید). به طور خاص، شما می توانید تعیین کنید که کدام DLL در دسترس کاربر است، یا به کاربر اجازه دهید انتخاب کند که کدام DLL را بارگیری کند. بنابراین، می توانید از DLL های مختلف استفاده کنید که عملکردهای یکسانی را اجرا می کنند و اقدامات مختلفی را انجام می دهند. به عنوان مثال، یک برنامه کاربردی طراحی شده برای انتقال داده ها به طور مستقل می تواند در زمان اجرا تصمیم بگیرد که آیا DLL برای TCP / IP یا پروتکل دیگری بارگیری شود.

بارگیری یک DLL معمولی

اولین کاری که باید هنگام بارگذاری پویا یک DLL انجام دهید، قرار دادن ماژول کتابخانه در حافظه پردازش است. این عملیات با استفاده از تابع انجام می شود :: LoadLibraryکه دارای یک آرگومان واحد است - نام ماژول برای بارگذاری. قطعه مربوطه از برنامه باید به شکل زیر باشد:

HINSTANCE hMyDll; :: اگر ((hMyDll = :: LoadLibrary ("MyDLL")) == NULL) (/ * DLL بارگیری نشد * /) در غیر این صورت (/ * برنامه می تواند از توابع DLL از طریق hMyDll * / استفاده کند)

ویندوز dll را پسوند استاندارد برای یک کتابخانه می‌داند مگر اینکه پسوند دیگری را مشخص کنید. اگر مسیر در نام فایل نیز مشخص شده باشد، فقط از آن برای جستجوی فایل استفاده می شود. در غیر این صورت، ویندوز فایل را به همان روشی که در مورد DLL های متصل به طور ضمنی وجود دارد، از دایرکتوری که فایل exe از آن بارگذاری می شود شروع می کند و مطابق با مقدار PATH ادامه می دهد.

هنگامی که ویندوز فایل را پیدا می کند، مسیر کامل آن با مسیر DLL هایی که قبلاً توسط این فرآیند بارگذاری شده اند مقایسه می شود. اگر هویتی پیدا شود، به جای بارگیری یک کپی از برنامه، یک دسته به کتابخانه از قبل پیوند داده شده برگردانده می شود.

اگر فایل پیدا شد و کتابخانه با موفقیت بارگیری شد، تابع :: LoadLibraryدسته خود را برمی گرداند ، که برای دسترسی به توابع کتابخانه استفاده می شود.

قبل از استفاده از توابع کتابخانه، باید آدرس آنها را دریافت کنید. برای انجام این کار ، ابتدا از دستورالعمل استفاده کنید typedefبرای تعریف نوع اشاره گر تابع و تعریف متغیری از این نوع جدید ، به عنوان مثال:

// نوع PFN_MyFunction نشانگر یک تابع را اعلام می کند // نشانگر را به بافر کاراکتر می پذیرد و مقدار int را تایپ می کند :: PFN_MyFunction pfnMyFunction;

سپس باید توصیفگر کتابخانه را دریافت کنید، که با آن می توانید آدرس توابع را تعیین کنید، به عنوان مثال، آدرس تابعی به نام MyFunction:

HMyDll = :: LoadLibrary ("MyDLL"); pfnMyFunction = (PFN_MyFunction) :: GetProcAddress (hMyDll ، "MyFunction") ؛ :: int iCode = (* pfnMyFunction) ("سلام");

آدرس تابع با استفاده از تابع تعیین می شود :: GetProcAddress، باید نام کتابخانه و نام تابع ارسال شود. مورد دوم باید به شکلی که از DLL صادر می شود منتقل شود.

همچنین می توانید به تابع که در آن صادر می شود اشاره کنید (در این مورد ، برای ایجاد کتابخانه باید از یک فایل def استفاده شود ، که بعداً مورد بحث قرار می گیرد):

PfnMyFunction = (PFN_MyFunction) :: GetProcAddress (hMyDll ، MAKEINTRESOURCE (1)) ؛

پس از اتمام کار با کتابخانه پیوند پویا، می توان آن را از حافظه پردازش با استفاده از تابع :: کتابخانه رایگان:

:: FreeLibrary (hMyDll);

بارگیری برنامه های افزودنی MFC DLL

هنگام بارگیری پسوندهای MFC برای DLL (که در زیر با جزئیات بیشتر مورد بحث قرار گرفته است) به جای توابع LoadLibraryو کتابخانه رایگانتوابع استفاده می شود AfxLoadLibraryو AfxFreeLibrary... دومی تقریباً با عملکردهای Win32 API یکسان است. آنها فقط علاوه بر این تضمین می کنند که ساختارهای MFC اولیه شده با پسوند DLL توسط نخ های دیگر دستکاری نمی شوند.

منابع DLL

بارگذاری پویا برای منابع DLL نیز اعمال می شود که MFC از آنها برای بارگیری منابع برنامه استاندارد استفاده می کند. برای این کار ابتدا باید تابع را فراخوانی کنید LoadLibraryو DLL را در حافظه قرار دهید. سپس با استفاده از تابع AfxSetResourceHandleشما باید پنجره برنامه را برای دریافت منابع از کتابخانه تازه بارگذاری شده آماده کنید. در غیر این صورت، منابع از فایل های متصل به فایل اجرایی فرآیند بارگذاری می شوند. این رویکرد زمانی مناسب است که شما نیاز به استفاده از مجموعه های مختلف منابع، به عنوان مثال، برای زبان های مختلف دارید.

اظهار نظر. با استفاده از تابع LoadLibraryهمچنین می توانید فایل های اجرایی را در حافظه بارگذاری کنید (آنها را برای اجرا اجرا نکنید!). سپس می توان از توصیفگر اجرایی هنگام فراخوانی توابع استفاده کرد FindResourceو LoadResourceبرای یافتن و بارگیری منابع برنامه همچنین با استفاده از عملکرد، ماژول ها را از حافظه خارج کنید کتابخانه رایگان.

نمونه ای از یک DLL معمولی و روش های بارگذاری

در اینجا کد منبع یک کتابخانه پیوند پویا به نام MyDLL است که حاوی یک تابع به نام MyFunction است که به سادگی یک پیام را نمایش می دهد.

ابتدا یک macroconstant در فایل هدر تعریف می شود صادرات... با استفاده از این کلمه کلیدیبا تعریف یک تابع، DLL به شما این امکان را می دهد که به پیوند دهنده بگویید که تابع برای استفاده توسط برنامه های دیگر در دسترس است و باعث می شود آن را به کتابخانه واردات اضافه کند. علاوه بر این، چنین تابعی، درست مانند یک رویه پنجره، باید با استفاده از ثابت تعریف شود برگشت به تماس:

MyDLL.h#define EXPORT extern "C" __declspec (dllexport) EXPORT int CALLBACK MyFunction (char * str);

فایل کتابخانه نیز کمی متفاوت از فایل های معمولی C برای ویندوز است. به جای یک تابع در آن WinMainیک تابع وجود دارد DllMain... این تابع برای انجام مقداردهی اولیه استفاده می شود که بعداً مورد بحث قرار می گیرد. برای اینکه کتابخانه پس از بارگذاری در حافظه باقی بماند و بتواند توابع خود را فراخوانی کند، مقدار بازگشتی آن باید TRUE باشد:

MyDLL.c #include #include "MyDLL.h" int WINAPI DllMain (HINSTANCE hInstance, DWORD fdReason, PVOID pvReserved) (return TRUE;) EXPORT int CALLBACK MyFunction (char * str) (MessageBox (NULL, DOK, stroll); بازگشت 1؛)

پس از ترجمه و پیوند این فایل ها، دو فایل ظاهر می شود - MyDLL.dll (خود کتابخانه پیوند پویا) و MyDLL.lib (کتابخانه وارداتی آن).

نمونه ای از اتصال ضمنی DLL توسط یک برنامه

بیایید اکنون کد منبع یک برنامه کاربردی ساده را ارائه دهیم که از تابع MyFunction از کتابخانه MyDLL.dll استفاده می کند:

#عبارتند از #include "MyDLL.h" int WINAPI WinMain (HINSTANCE hInstance، HINSTANCE hPrevInstance، LPSTR lpCmdLine، int nCmdShow) (int iCode = MyFunction ("Hello")؛ بازگشت 0؛)

این برنامه به نظر می رسد برنامه های منظمبرای ویندوز، که واقعاً همینطور است. با این وجود، لازم به ذکر است که متن منبع آن علاوه بر فراخوانی تابع MyFunction از DLL-library، شامل فایل هدر این کتابخانه یعنی MyDLL.h نیز می باشد. همچنین لازم است در مرحله ساخت اپلیکیشن به آن متصل شوید کتابخانه وارداتی MyDLL.lib (فرایند اتصال ضمنی یک DLL به یک ماژول اجرایی).

درک این نکته بسیار مهم است که کد MyFunction خود در فایل MyApp.exe گنجانده نشده است. در عوض، به سادگی پیوندی به فایل MyDLL.dll و پیوندی به تابع MyFunction، که در آن فایل است، دارد. فایل MyApp.exe برای اجرا به فایل MyDLL.dll نیاز دارد.

فایل هدر MyDLL.h در فایل منبع برنامه MyApp.c وجود دارد، همانطور که فایل windows.h در آنجا گنجانده شده است. گنجاندن کتابخانه واردات MyDLL.lib برای پیوند، شبیه به گنجاندن تمام کتابخانه‌های وارداتی ویندوز است. هنگامی که MyApp.exe در حال اجرا است، به MyDLL.dll متصل می شود، درست مانند همه DLL های استاندارد ویندوز.

نمونه ای از بارگذاری پویا یک DLL توسط یک برنامه

بیایید اکنون کد منبع کامل یک برنامه کاربردی ساده را ارائه دهیم که از تابع MyFunction از کتابخانه MyDLL.dll با استفاده از بارگذاری پویا کتابخانه استفاده می کند:

#عبارتند از typedef int (WINAPI * PFN_MyFunction) (char *); int WINAPI WinMain (HINSTANCE hInstance، HINSTANCE hPrevInstance، LPSTR lpCmdLine، int nCmdShow) (HINSTANCE hMyDll؛ if ((hMyDll = LoadLibrary ("MyDLL")) = "MyDNFunt") = "MyDNFunt"); * pfnMyFunction) ("سلام")؛ FreeLibrary (hMyDll)؛ بازگشت 0؛)

ایجاد DLL

اکنون که می دانید DLL ها در برنامه ها چگونه کار می کنند ، بیایید نحوه ایجاد آنها را بررسی کنیم. هنگام توسعه یک برنامه کاربردی، توصیه می شود توابعی را که توسط چندین فرآیند قابل دسترسی هستند در یک DLL قرار دهید. این امکان استفاده کارآمدتر از حافظه در ویندوز را فراهم می کند.

ساده ترین راه برای ایجاد یک پروژه DLL جدید استفاده از AppWizard است که بسیاری از عملیات را به طور خودکار انجام می دهد. برای DLL های ساده مانند آنچه در این فصل مورد بحث قرار گرفت، باید نوع پروژه Win32 Dynamic-Link Library را انتخاب کنید. به پروژه جدید تمام پارامترهای لازم برای ایجاد DLL اختصاص داده می شود. فایل های منبع باید به صورت دستی به پروژه اضافه شوند.

اگر قصد استفاده کامل از آن را دارید عملکرد MFC مانند اسناد و نماها یا قصد ایجاد سرور OLE Automation را دارید، بهتر است نوع پروژه MFC AppWizard (dll) را انتخاب کنید. در این حالت، جادوگر علاوه بر تخصیص پارامترهایی به پروژه برای اتصال کتابخانه های پویا، کارهای اضافی انجام خواهد داد. پروژه ارجاعات لازم را به کتابخانه‌های MFC و فایل‌های منبع حاوی توضیحات و پیاده‌سازی در DLL شی کلاس برنامه‌ای که از CWinApp.

گاهی اوقات راحت است که ابتدا یک پروژه MFC AppWizard (dll) به عنوان یک برنامه آزمایشی ایجاد کنید و سپس یک DLL به عنوان جزء آن ایجاد کنید. در نتیجه ، در صورت لزوم ، DLL به طور خودکار ایجاد می شود.

عملکرد DllMain

اکثر DLL ها به سادگی مجموعه ای از توابع هستند که تقریباً مستقل از یکدیگر هستند که صادر شده و در برنامه ها استفاده می شوند. علاوه بر توابع در نظر گرفته شده برای صادرات، هر DLL دارای یک تابع است DllMain... این تابع برای مقداردهی اولیه و پاکسازی DLLها طراحی شده است. او توابع را جایگزین کرد LibMainو WEPدر نسخه های قبلی ویندوز استفاده شده است. ساختار ساده ترین تابع DllMainممکن است شبیه به این باشد:

BOOL WINAPI DllMain (HANDLE hInst، DWORD dwReason، LPVOID IpReserved) (BOOL bAllWentWell = TRUE؛ سوئیچ (dwReason) (مورد DLL_PROCESS_ATTACH: // مقداردهی اولیه فرآیند. Break؛ مورد DLL_THREAITALIZE //ATTACH Initialize ///ATLL_ DLL_ETTH: break stream structures.break؛ case DLL_PROCESS_DETACH: // Clearing process structures.break;) if (bAllWentWell) بازگشت TRUE؛ در غیر این صورت بازگشت FALSE؛)

عملکرد DllMainدر چندین مورد تماس گرفته شده است. دلیل فراخوانی آن با پارامتر dwReason مشخص می شود که می تواند یکی از مقادیر زیر باشد.

اولین باری که DLL توسط فرآیند بارگیری می شود، تابع فراخوانی می شود DllMainبا dwReason برابر با DLL_PROCESS_ATTACH. هر بار که یک فرآیند یک رشته جدید ایجاد می کند، DllMainO با dwReason برابر با DLL_THREAD_ATTACH فراخوانی می شود (به جز اولین رشته، زیرا در این حالت dwReason برابر با DLL_PROCESS_ATTACH است).

در پایان فرآیند با DLL ، تابع DllMainبا dwReason برابر DLL_PROCESS_DETACH فراخوانی می شود. هنگامی که یک رشته (غیر از اولین) از بین می رود، dwReason برابر با DLL_THREAD_DETACH خواهد بود.

همه عملیات راه اندازی اولیه و پاکسازی برای فرآیندها و نخ هایی که DLL به آنها نیاز دارد باید بر اساس مقدار dwReason انجام شود ، همانطور که در مثال قبلی نشان داده شده است. مقداردهی اولیه فرآیند معمولاً به تخصیص منابع به اشتراک گذاشته شده محدود می‌شود، مانند بارگیری فایل‌های مشترک و راه‌اندازی کتابخانه‌ها. مقداردهی اولیه رشته فقط برای پیکربندی حالت های خاص این رشته استفاده می شود، به عنوان مثال، برای مقداردهی اولیه حافظه محلی.

یک DLL می تواند شامل منابعی باشد که متعلق به برنامه تماس گیرنده نیستند. اگر توابع DLL بر روی منابع DLL کار کنند، بدیهی است که توصیفگر hInst در مکانی مخفی ذخیره شود و از آن هنگام بارگیری منابع از DLL استفاده شود. اشاره گر IpReserved برای داخلی رزرو شده است با استفاده از ویندوز... بنابراین ، برنامه نباید آن را ادعا کند. شما فقط می توانید معنی آن را بررسی کنید. اگر DLL به صورت پویا بارگذاری شده باشد، NULL خواهد بود. هنگامی که به صورت ایستا بارگذاری می شود، این اشاره گر غیر تهی خواهد بود.

در صورت موفقیت، عملکرد DllMainباید TRUE را برگرداند. اگر خطایی رخ دهد، FALSE برگردانده می شود و اقدامات بعدی خاتمه می یابد.

اظهار نظر. اگر تابع DllMain () خود را ننویسید، کامپایلر نسخه استاندارد را شامل می شود که به سادگی TRUE را برمی گرداند.

صادرات توابع از DLL

برای دسترسی یک برنامه کاربردی به توابع DLL، هر یک از آنها باید یک ردیف در جدول توابع صادر شده DLL اشغال کند. دو روش برای افزودن یک تابع به این جدول در زمان کامپایل وجود دارد.

__ روش Declspec (dllexport)

شما می توانید یک تابع را از یک DLL با قرار دادن اصلاح کننده __declspec (dllexport) در ابتدای توضیحات آن صادر کنید. علاوه بر این، MFC شامل چندین ماکرو است که __declspec (dllexport) را تعریف می کنند، از جمله AFX_CLASS_EXPORT، AFX_DATA_EXPORT، و AFX_API_EXPORT.

روش __declspec کمتر از روش دوم استفاده می‌شود، که با فایل‌های تعریف ماژول (.def) کار می‌کند و امکان کنترل بهتر بر فرآیند صادرات را فراهم می‌کند.

فایل های تعریف ماژول

نحو فایل های .def در Visual C ++ نسبتاً ساده است، عمدتاً به این دلیل که پارامترهای پیچیده استفاده شده در نسخه های قبلیویندوز در Win32 دیگر اعمال نمی شود. همانطور که از مثال ساده زیر مشخص می شود، فایل .def شامل نام و توضیحات کتابخانه و همچنین لیستی از توابع صادر شده است:

MyDLL.def Library "MyDLL" توضیحات "MyDLL - نمونه DLL" Exports MyFunction @ 1

در خط صادرات یک تابع، می توانید شماره ترتیبی آن را با علامت @ مشخص کنید. سپس هنگام مراجعه به این شماره استفاده می شود GetProcAddress(). در واقع، کامپایلر اعداد ترتیبی را به تمام اشیاء صادر شده اختصاص می دهد. با این حال، روشی که آن را انجام می دهد تا حدودی غیرقابل پیش بینی است، مگر اینکه این اعداد به صراحت اختصاص داده شوند.

می توانید از پارامتر NONAME در خط صادرات استفاده کنید. از وارد کردن نام تابع در جدول صادرات DLL توسط کامپایلر جلوگیری می کند:

MyFunction @ 1 NONAME

گاهی اوقات می توانید فضای زیادی را در فایل DLL ذخیره کنید. برنامه هایی که از کتابخانه import برای پیوند ضمنی DLL استفاده می کنند، تفاوت را متوجه نمی شوند زیرا پیوند ضمنی از اعداد دنباله به طور خودکار استفاده می کند. برنامه هایی که DLL ها را به صورت پویا بارگذاری می کنند باید وارد شوند GetProcAddressیک شماره دنباله، نه نام تابع.

برای مثال، هنگام استفاده از موارد فوق، فایل def که عملکردهای صادر شده کتابخانه DLL را توصیف می کند، ممکن است متفاوت باشد:

#define EXPORT extern "C" __declspec (dllexport) EXPORT int CALLBACK MyFunction (char * str); a مانند این: خارجی "C" int CALLBACK MyFunction (char * str);

صادرات کلاس ها

ایجاد یک فایل .def برای صادرات حتی کلاس های ساده از یک کتابخانه پویا می تواند بسیار مشکل باشد. شما باید به صراحت هر تابعی را که می تواند توسط یک برنامه خارجی استفاده شود صادر کنید.

اگر به فایل تخصیص حافظه پیاده سازی شده در کلاس نگاه کنید، متوجه عملکردهای بسیار غیرعادی در آن خواهید شد. به نظر می رسد که سازنده و تخریب کننده ضمنی، توابع اعلام شده در ماکروهای MFC، به ویژه _DECLARE_MESSAGE_MAP، و همچنین توابع نوشته شده توسط برنامه نویس وجود دارد.

در حالی که امکان صادرات هر یک از این توابع به صورت جداگانه وجود دارد ، راه ساده تری وجود دارد. در صورت استفاده از اصلاح کننده ماکرو AFX_CLASS_EXPORT در اعلان کلاس ، کامپایلر خود صادرات را انجام می دهد. توابع لازمبه برنامه اجازه می دهد از کلاس موجود در DLL استفاده کند.

حافظه DLL

برخلاف کتابخانه‌های استاتیک، که اساساً بخشی از کد برنامه‌ها هستند، کتابخانه‌های پیوند پویا در نسخه‌های 16 بیتی ویندوز، حافظه را به روشی متفاوت مدیریت می‌کنند. تحت Win 16، حافظه DLL خارج از فضای آدرس وظیفه قرار داشت. قرار دادن کتابخانه های پویا در حافظه جهانی امکان اشتراک گذاری آنها را با وظایف مختلف فراهم می کند.

در Win32، یک DLL در ناحیه حافظه فرآیندی قرار دارد که آن را بارگذاری می کند. به هر فرآیند یک کپی جداگانه از حافظه "جهانی" DLL داده می شود، که هر بار که یک فرآیند جدید آن را بارگذاری می کند، مقدار اولیه مجدد آن داده می شود. این بدان معناست که یک کتابخانه پیوند پویا را نمی توان در حافظه مشترک، همانطور که در Winl6 بود، به اشتراک گذاشت.

و با این حال، با انجام برخی دستکاری‌های پیچیده در بخش داده‌های DLL، می‌توانید یک ناحیه حافظه مشترک برای همه فرآیندها با استفاده از این کتابخانه ایجاد کنید.

فرض کنید شما یک آرایه از اعداد صحیح دارید که باید توسط همه فرایندهایی که این DLL را بارگیری می کنند استفاده شود. این را می توان به شرح زیر برنامه ریزی کرد:

#pragma data_seg (. myseg") int sharedlnts; // متغیرهای دیگر استفاده مشترک#pragma data_seg () #pragma comment (lib، "msvcrt" "-SECTION: .myseg، rws")؛

همه متغیرهای اعلام شده بین دستورالعمل های #pragma data_seg () در بخش myseg. قرار می گیرند. دستورالعمل #pragma comment () یک نظر عادی نیست. به کتابخانه زمان اجرا C دستور می دهد که پارتیشن جدید را به عنوان خواندن / نوشتن / اشتراک گذاری علامت گذاری کند.

کامپایل DLL کامل

اگر پروژه DLL با استفاده از AppWizard ایجاد شود و فایل .def بر این اساس اصلاح شود ، این کافی است. اگر فایل های پروژه را به صورت دستی یا به روش های دیگر بدون استفاده از AppWizard ایجاد می کنید، باید گزینه / DLL را در خط فرمان لینکر قرار دهید. این یک DLL به جای یک فایل اجرایی مستقل ایجاد می کند.

اگر یک خط LIBRART در فایل .def وجود دارد، نیازی نیست که گزینه / DLL را به صراحت در خط فرمان لینکر مشخص کنید.

تعدادی حالت ویژه برای MFC مربوط به استفاده از کتابخانه پیوند پویا MFC وجود دارد. بخش بعدی به این موضوع اختصاص دارد.

DLL و MFC

هنگام ایجاد کتابخانه های پیوند پویا، برنامه نویس نیازی به استفاده از MFC ندارد. با این حال ، استفاده از MFC چندین امکان بسیار مهم را باز می کند.

دو سطح استفاده از ساختار MFC در یک DLL وجود دارد. اولین کتابخانه یک کتابخانه پیوند پویای معمولی مبتنی بر MFC است ، MFC DLL(DLL MFC معمولی). می تواند از MFC استفاده کند، اما نمی تواند نشانگرهای شی MFC را بین DLL ها و برنامه ها ارسال کند. سطح دوم در اجرا شده است پسوندهای پویا MFC(DLL پسوند MFC). استفاده از این نوع کتابخانه پیوند پویا نیاز به پیکربندی اضافی دارد، اما به شما امکان می دهد آزادانه اشاره گرها را به اشیاء MFC بین DLL و برنامه مبادله کنید.

DLL های رایج MFC

DLL های معمولی MFC به شما امکان می دهند از MFC در کتابخانه های پیوند پویا استفاده کنید. با این حال، برنامه هایی که به چنین کتابخانه هایی دسترسی دارند نیازی به ساخت بالای MFC ندارند. در DLL های معمولی، می توانید از MFC به هر شکلی که می خواهید استفاده کنید، از جمله ایجاد کلاس های جدید مبتنی بر MFC در DLL و صادرات آنها به برنامه ها.

با این حال، DLL های معمولی نمی توانند اشاره گر را به کلاس های مشتق شده از MFC با برنامه ها مبادله کنند.

اگر برنامه شما نیاز به مبادله اشاره گر به اشیاء کلاس های MFC یا مشتقات آنها با DLL دارد، باید از پسوند DLL که در بخش بعدی توضیح داده شده است استفاده کنید.

معماری معمول DLL برای استفاده در محیط های برنامه نویسی دیگر مانند Visual Basic و PowerBuilder طراحی شده است.

هنگام ایجاد یک DLL MFC معمولی با استفاده از AppWizard، یک پروژه جدید از نوع MFC App Wizard (dll)... در اولین کادر محاوره‌ای Application Wizard، باید یکی از حالت‌های کتابخانه‌های پویا رایج را انتخاب کنید: "DLL منظم با MFC آماری مرتبط" یا "DLL منظم با استفاده از DLL مشترک MFC". اولی اتصال ایستا و دومی پیوند پویا کتابخانه های MFC را فراهم می کند. بعداً می‌توانید حالت اتصال MFC را به DLL با استفاده از جعبه ترکیبی در برگه عمومی در کادر گفتگوی تنظیمات پروژه تغییر دهید.

مدیریت اطلاعات وضعیت MFC

هر ماژول فرآیند MFC حاوی اطلاعاتی در مورد وضعیت آن است. بنابراین، اطلاعات وضعیت یک DLL با اطلاعات وضعیت برنامه فراخوانی متفاوت است. بنابراین، هر توابعی که از کتابخانه صادر می‌شود و مستقیماً از برنامه‌ها به آنها دسترسی پیدا می‌کند باید به MFC بگوید که از چه اطلاعاتی باید استفاده کند. در یک DLL MFC معمولی که از MFC DLL استفاده می کند، قبل از فراخوانی زیربرنامه MFC، خط زیر را در ابتدای تابع صادر شده قرار دهید:

AFX_MANAGE_STATE (AfxGetStaticModuleState ());

این عبارت استفاده از اطلاعات وضعیت مناسب را در حین اجرای تابع فراخوانده زیر روال مشخص می کند.

پسوندهای پویا MFC

MFC به شما امکان می دهد DLL هایی ایجاد کنید که برنامه های شما به عنوان مجموعه ای از توابع جداگانه فکر نمی کنند، بلکه به عنوان پسوند MFC هستند. با این نوع DLL، می توانید کلاس های جدیدی ایجاد کنید که از کلاس های MFC مشتق شده و در برنامه های خود استفاده کنید.

برای اینکه نشانگرهای شی MFC آزادانه بین برنامه و DLL رد و بدل شوند، باید یک پسوند MFC پویا ایجاد کنید. DLL های این نوع به DLL های MFC مانند هر برنامه ای که از پسوند پویا MFC استفاده می کند متصل می شود.

ساده ترین راه برای ایجاد یک افزونه جدید پویا MFC استفاده از Application Wizard برای تنظیم نوع پروژه است. MFC App Wizard (dll)و در مرحله 1 حالت "MFC Extension DLL" را فعال کنید. این همه ویژگی های مورد نیاز پسوند پویا MFC را به پروژه جدید اختصاص می دهد. علاوه بر این، یک تابع ایجاد خواهد شد DllMainبرای یک DLL که تعدادی عملیات خاص را برای مقداردهی اولیه یک پسوند DLL انجام می دهد. لطفا توجه داشته باشید که کتابخانه های پویا از این نوعشامل اشیاء مشتق شده از آن نباشد و نباید باشد CWinApp.

راه اندازی برنامه های افزودنی پویا

برای «تطبیق» در چارچوب MFC، پسوندهای پویا MFC به موارد اضافی نیاز دارند راه اندازی اولیه... عملیات مربوطه توسط تابع انجام می شود DllMain... بیایید به مثالی از این تابع ایجاد شده توسط AppWizard نگاه کنیم.

Static AFX_EXTENSION_MODULE MyExtDLL = (NULL، NULL); خارجی "C" int APIENTRY DllMain (HINSTANCE hinstance، DWORD dwReason، LPVOID IpReserved) (اگر (dwReason == DLL_PROCESS_ATTACH) (TRACED ("MYEXT.DLL Initializing! \ n")؛ // onetime Induitization DLL نکته)؛ // این DLL را در CDynLinkLibrary جدید (MyExtDLL) در زنجیره منبع قرار دهید؛) در غیر این صورت اگر (dwReason == DLL_PROCESS_DETACH) (TRACED ("MYEXT.DLL در حال پایان است! \ n")؛) 1 را برگردانید؛ // ok)

مهمترین بخش این تابع فراخوانی است AfxInitExtensionModule... این مقداردهی اولیه کتابخانه پیوند پویا است که به آن اجازه می دهد به عنوان بخشی از چارچوب MFC به درستی کار کند. آرگومان های این تابع عبارتند از توصیفگر DLL که به DllMain و ساختار AFX_EXTENSION_MODULE ارسال شده است که حاوی اطلاعاتی در مورد MFC DLL است.

نیازی به مقداردهی اولیه ساختار AFX_EXTENSION_MODULE نیست. با این حال، باید اعلام شود. سازنده با مقداردهی اولیه سر و کار خواهد داشت. کتابخانه CDynLinkLibrary... در DLL باید یک کلاس ایجاد کنید کتابخانه CDynLinkLibrary... سازنده آن نه تنها ساختار AFX_EXTENSION_MODULE را مقداردهی اولیه می کند، بلکه کتابخانه جدید را به لیست DLL هایی که MFC می تواند با آنها کار کند اضافه می کند.

بارگیری پسوندهای پویا MFC

شروع از نسخه 4.0 MFC امکان بارگیری و تخلیه پویا DLLها از جمله برنامه های افزودنی را فراهم می کند. برای انجام صحیح این عملیات بر روی DLL در حال ایجاد، به عملکرد آن DllMainدر لحظه قطع ارتباط از فرآیند، باید یک تماس اضافه کنید AfxTermExtensionModule... ساختار AFX_EXTENSION_MODULE که قبلاً در بالا استفاده شده است به آخرین تابع به عنوان پارامتر ارسال می شود. برای انجام این کار ، در متن DllMainشما باید خطوط زیر را اضافه کنید.

اگر (dwReason == DLL_PROCESS_DETACH) (AfxTermExtensionModule (MyExtDLL)؛)

همچنین، به یاد داشته باشید که DLL جدید یک پسوند پویا است و باید با استفاده از توابع به صورت پویا بارگیری و تخلیه شود. AfxLoadLibraryو AfxFreeLibrary،اما نه LoadLibraryو کتابخانه رایگان.

صادرات توابع از پسوندهای پویا

حالا بیایید ببینیم که چگونه توابع و کلاس ها از یک پسوند پویا به یک برنامه صادر می شوند. اگرچه می‌توانید به صورت دستی همه نام‌های توسعه‌یافته را به فایل DEF اضافه کنید، بهتر است از اصلاح‌کننده‌ها برای اعلان‌های کلاس‌ها و توابع صادر شده، مانند AFX_EXT_CLASS و AFX_EXT_API استفاده کنید، برای مثال:

کلاس AFX_EXT_CLASS CMyClass: cobject عمومی (// اعلامیه کلاس شما) void AFX_EXT_API MyFunc ();

دو راه برای بارگذاری یک DLL وجود دارد: پیوند صریح و پیوند ضمنی.

پیوند ضمنی، عملکردهای DLL بارگذاری شده را به بخش واردات فایل فراخوانی اضافه می کند. هنگام راه اندازی چنین فایلی، لودر سیستم عاملبخش واردات را تجزیه و تحلیل می کند و تمام کتابخانه های مشخص شده را به هم متصل می کند. به دلیل سادگی ، این روش بسیار محبوب است. اما سادگی، سادگی است، و پیوند ضمنی دارای معایب و محدودیت‌های خاصی است:

همه DLL های متصل همیشه بارگذاری می شوند، حتی اگر در طول کل جلسه برنامه هرگز هیچ یک از آنها را فراخوانی نکند.

اگر حداقل یکی از DLL های مورد نیاز وجود ندارد (یا DLL حداقل یک تابع مورد نیاز را صادر نمی کند) - بارگیری فایل اجرایی با پیام "کتابخانه پیوند پویا یافت نشد" (یا چیزی شبیه به آن) قطع می شود - حتی اگر نبود این DLL برای اجرای برنامه حیاتی نیست. مثلا، ویرایشگر متنمی تواند به خوبی در یک پیکربندی حداقلی کار کند - بدون ماژول چاپ، خروجی جداول، نمودارها، فرمول ها و سایر اجزای جزئی، اما اگر این DLL ها با یک پیوند ضمنی بارگذاری شوند - آن را دوست داشته باشید یا نه، باید آنها را "کشش" کنید. .

جستجوهای DLL به ترتیب زیر انجام می شود: در دایرکتوری حاوی فایل فراخوانی. در فهرست فعلی فرآیند؛ v دایرکتوری سیستم% ویندوز % سیستم %; در دایرکتوری اصلی% Windows%; در دایرکتوری های مشخص شده در متغیر PATH. تعیین مسیر جستجوی متفاوت غیرممکن است (یا بهتر است بگوییم، شاید، اما این نیاز به ایجاد تغییرات در رجیستری سیستم دارد، و این تغییرات بر تمام فرآیندهای در حال اجرا در سیستم تأثیر می گذارد - که خوب نیست).

پیوند صریح همه این معایب را از بین می برد - به قیمت برخی از پیچیدگی های کد. خود برنامه نویس باید مراقب بارگیری DLL و اتصال توابع صادر شده باشد (کنترل خطاها را فراموش نکنید، در غیر این صورت یک روز سیستم منجمد می شود). از سوی دیگر، پیوند صریح اجازه می دهد تا DLL ها در صورت نیاز بارگذاری شوند و به برنامه نویس این توانایی را می دهد که به طور مستقل موقعیت هایی را که DLL ها از دست رفته اند، مدیریت کند. می توانید بیشتر بروید - نام DLL را به صراحت در برنامه تنظیم نکنید، بلکه فلان دایرکتوری را برای وجود کتابخانه های پویا اسکن کنید و همه یافت شده را به برنامه متصل کنید. مکانیزم پشتیبانی پلاگین در مدیریت فایل محبوب FAR (و نه تنها در آن) به این صورت است.

سه راه برای بارگذاری DLL وجود دارد:

الف) ضمنی؛

ج) به تعویق افتاد.

بارگذاری DLL ضمنی را در نظر بگیرید. برای ساختن برنامه ای که بر بارگذاری ضمنی DLL متکی است ، باید موارد زیر را داشته باشید:

کتابخانه شامل پرونده ای با توضیحات اشیاء DLL استفاده شده (نمونه های اولیه عملکرد ، اعلانات کلاس و نوع) است. این فایل توسط کامپایلر استفاده می شود.

فایل LIB با لیستی از شناسه های وارد شده. این فایل باید به تنظیمات پروژه (به لیست کتابخانه های استفاده شده توسط لینکر) اضافه شود.

پروژه به روش معمول تدوین شده است. با استفاده از ماژول های شی و فایل LIB، و همچنین در نظر گرفتن پیوندها به شناسه های وارد شده، پیوند دهنده (لینکر، پیوند دهنده) یک ماژول EXE قابل بارگذاری تولید می کند. در این ماژول ، پیوند دهنده یک بخش واردات قرار می دهد که نام همه ماژول های DLL مورد نیاز را لیست می کند. برای هر DLL، بخش import مشخص می کند که نام تابع و متغیر در کد فایل اجرایی ارجاع داده شود. لودر سیستم عامل از این اطلاعات استفاده خواهد کرد.

در مرحله اجرای برنامه مشتری چه اتفاقی می افتد؟ پس از راه اندازی ماژول EXE، لودر سیستم عامل عملیات زیر را انجام می دهد:

1. یک فضای آدرس مجازی برای یک فرآیند جدید ایجاد می کند و یک ماژول اجرایی را روی آن پروژه می دهد.

2. بخش واردات را تجزیه و تحلیل می کند، همه ماژول های DLL مورد نیاز را شناسایی می کند و همچنین آنها را در فضای آدرس فرآیند نمایش می دهد.

یک DLL می تواند توابع و متغیرها را از یک DLL دیگر وارد کند. این بدان معناست که ممکن است بخش واردات خود را داشته باشد که برای آن باید همان مراحل را تکرار کنید. در نتیجه، زمان زیادی طول می کشد تا فرآیند اولیه شود.

پس از اینکه ماژول EXE و تمام DLL ها به فضای آدرس فرآیند نگاشت شدند، رشته اصلی آن برای اجرا آماده است و برنامه شروع به اجرا می کند.

معایب بارگذاری ضمنی، بارگذاری اجباری کتابخانه است، حتی اگر توابع آن فراخوانی نشود، و بر این اساس، الزام اجباری برای حضور کتابخانه در هنگام پیوند است.

بارگذاری صریح DLL معایب ذکر شده در بالا را با هزینه برخی از پیچیدگی کد حذف می کند. خود برنامه نویس باید مراقب بارگذاری DLL و اتصال توابع صادر شده باشد. از سوی دیگر، بارگذاری صریح به DLL اجازه می دهد تا در صورت نیاز بارگذاری شود و برنامه را قادر می سازد تا موقعیت هایی را که در غیاب DLL ایجاد می شوند، مدیریت کند.

در مورد بارگذاری صریح، فرآیند کار با DLL در سه مرحله انجام می شود:

1. بارگیری DLL با استفاده از یک تابع LoadLibrary(یا همتای توسعه یافته آن LoadLibraryEx).اگر بارگذاری موفقیت آمیز باشد، تابع یک توصیفگر hLib از نوع HMODULE را برمی‌گرداند که امکان دسترسی بیشتر به این DLL را فراهم می‌کند.

2. فراخوانی تابع GetProcAddressبرای دریافت اشاره گر به توابع مورد نیاز یا اشیاء دیگر. به عنوان اولین پارامتر، تابع GetProcAddressیک توصیفگر hLib را به عنوان پارامتر دوم دریافت می کند - آدرس رشته با نام تابع وارد شده. سپس اشاره گر به دست آمده توسط مشتری استفاده می شود. به عنوان مثال، اگر یک نشانگر تابع باشد، تابع مورد نیاز فراخوانی می شود.

3. هنگامی که کتابخانه پویا بارگذاری شده دیگر مورد نیاز نیست، توصیه می شود آن را با فراخوانی تابع آزاد کنید. کتابخانه رایگانآزاد کردن یک کتابخانه به این معنی نیست که سیستم عامل بلافاصله آن را از حافظه حذف می کند. تأخیر تخلیه برای مواردی ارائه می شود که همان DLL پس از مدتی دوباره توسط برخی فرآیندها مورد نیاز باشد. اما اگر رم مشکلی داشته باشد، ویندوز ابتدا کتابخانه های آزاد شده را از حافظه حذف می کند.

بارگذاری تنبلی یک DLL را در نظر بگیرید. یک DLL تاخیری یک DLL به طور ضمنی پیوند خورده است که تا زمانی که کد به شناسه ای که از آن صادر شده است ارجاع دهد بارگذاری نمی شود. چنین DLL هایی می توانند در شرایط زیر مفید باشند:

اگر یک برنامه از چندین DLL استفاده کند، مقداردهی اولیه آن می تواند زمان زیادی طول بکشد، که توسط لودر برای نمایش همه DLL ها در فضای آدرس فرآیند مورد نیاز است. DLL های Lazy-load این مشکل را با توزیع بار DLL در طول اجرای برنامه حل می کنند.

اگر برنامه برای کار در نسخه های مختلف سیستم عامل طراحی شده باشد، ممکن است برخی از عملکردها فقط در نسخه های بعدی سیستم عامل ظاهر شوند و در نسخه فعلی استفاده نشوند. اما اگر برنامه تابع خاصی را فراخوانی نکند، DLL برای آن مورد نیاز نیست و می تواند با خیال راحت به کار خود ادامه دهد. هنگام اشاره به یک ناموجود

این تابع می تواند برای صدور اخطار برای کاربر فراهم کند.

برای اجرای روش بارگذاری تنبل، علاوه بر این لازم است که نه تنها کتابخانه واردات ضروری MyLib.lib، بلکه کتابخانه واردات سیستم delayimp.lib را نیز به لیست کتابخانه های پیوند دهنده اضافه کنید. علاوه بر این، باید پرچم / delayload: MyLib.dll را در گزینه های پیوند دهنده اضافه کنید.

این تنظیمات پیوند دهنده را مجبور می کند تا عملیات زیر را انجام دهد:

یک تابع خاص در ماژول EXE پیاده سازی کنید
delayLoadHelper ؛

MyLib.dll را از بخش import ماژول اجرایی حذف کنید تا لودر سیستم عامل در مرحله بارگیری برنامه سعی در بارگیری ضمنی این کتابخانه نداشته باشد.

یک بخش واردات معوق جدید به فایل EXE با لیستی از توابع وارد شده از MyLib.dll اضافه کنید.

تماس های تابع را از DLL به تماس تبدیل کنید
delayLoadhelper.

در مرحله اجرای برنامه، فراخوانی یک تابع از یک DLL با فراخوانی delayLoadHelper اجرا می شود. این تابع با استفاده از اطلاعات قسمت lazy import ابتدا LoadLibrary و سپس GetprocAddress را فراخوانی می کند. پس از دریافت آدرس تابع DLL، delayLoadHelper آن را طوری می سازد که در آینده این تابع DLL مستقیماً فراخوانی شود. توجه داشته باشید که هر تابع در یک DLL اولین باری که فراخوانی می شود به صورت جداگانه پیکربندی می شود.

استفاده DLL(کتابخانه پیوند پویا) در برنامه نویسی ویندوز گسترده است. DLLدر واقع بخشی از کد فایل اجرایی با پسوند DLL... هر برنامه ای می تواند تماس بگیرد DLL.

مزیت - فایده - سود - منفعت DLLبه شرح زیر است:

  • استفاده مجدد از کد
  • به اشتراک گذاری کد بین برنامه ها
  • تقسیم کد.
  • بهبود مصرف منابع در ویندوز

ایجاد DLL

در منو فایل New -> Other را انتخاب کنید. در کادر محاوره ای در برگه جدیدانتخاب کنید جادوگر DLL... یک ماژول به طور خودکار ایجاد می شود - یک الگوی خالی برای آینده DLL.

نحو DLL

ساختن DLL، انتخاب کنید پروژه -> ساخت نام پروژه .

قابلیت مشاهده عملکرد و رویه

توابع و رویه ها می توانند محلی باشند و از آن صادر شوند DLL.

محلی

توابع و رویه های محلی را می توان به صورت داخلی استفاده کرد DLL... آنها فقط در داخل کتابخانه قابل مشاهده هستند و هیچ برنامه ای نمی تواند آنها را از بیرون فراخوانی کند.

صادر شد

توابع و رویه های صادر شده را می توان در خارج از آن استفاده کرد DLL... برنامه های دیگر می توانند چنین توابع و رویه هایی را فراخوانی کنند.

کد منبع بالا از یک تابع صادر شده استفاده می کند. نام تابع از یک کلمه رزرو شده پیروی می کند صادرات.

بارگیری DLL

دو نوع بارگیری در دلفی وجود دارد DLL:

بارگذاری استاتیک

هنگامی که برنامه راه اندازی می شود، به طور خودکار بارگیری می شود. در طول اجرای برنامه در حافظه باقی می ماند. بسیار آسان برای استفاده. فقط یک کلمه اضافه کنید خارجیپس از اعلام عملکرد یا رویه

تابع SummaTotal (عامل: عدد صحیح): عدد صحیح; ثبت نام؛ خارجی "Example.dll"؛

اگر DLLیافت نشد، برنامه به اجرا ادامه خواهد داد.

در صورت نیاز در حافظه بارگذاری می شود. اجرای آن پیچیده تر است زیرا شما خودتان باید آن را از حافظه بارگیری و تخلیه کنید. از حافظه اقتصادی تر استفاده می شود، بنابراین برنامه سریعتر اجرا می شود. خود برنامه نویس باید مطمئن شود که همه چیز به درستی کار می کند. برای این شما نیاز دارید:

  • نوع عملکرد یا روش توصیف شده را اعلام کنید.
  • کتابخانه را در حافظه بارگذاری کنید.
  • آدرس یک تابع یا رویه را در حافظه دریافت کنید.
  • یک تابع یا رویه را فراخوانی کنید.
  • کتابخانه را از حافظه بارگیری کنید.

اعلان نوع که یک تابع را توصیف می کند

نوع TSumaTotal = تابع (ضریب: عدد صحیح): عدد صحیح;

در حال بارگیری کتابخانه

dll_instance: = LoadLibrary ("Example_dll.dll");

یک اشاره گر به یک تابع دریافت کنید

@SummaTotal: = GetProcAddress (dll_instance، "SummaTotal")؛

فراخوانی تابع

Label1.Caption: = IntToStr (SummaTotal (5));

تخلیه کتابخانه از حافظه

FreeLibrary (dll_instance)؛

تماس پویا DLLنیاز به کار بیشتری دارد ، اما مدیریت منابع در حافظه آسان تر است. اگر باید استفاده کنید DLLدر داخل برنامه، پس بارگذاری استاتیک ترجیح داده می شود. استفاده از بلوک را فراموش نکنید سعی کن... به جزو تلاش کن ... بالاخرهبرای جلوگیری از خطا در هنگام اجرای برنامه

صادرات رشته ها

خلق شده توسط DLLبا با استفاده از دلفی، می تواند در برنامه های نوشته شده به زبان های برنامه نویسی دیگر استفاده شود. به همین دلیل، ما نمی توانیم از هر نوع داده ای استفاده کنیم. انواعی که در دلفی وجود دارد ممکن است در زبان های دیگر وجود نداشته باشد. توصیه می شود از انواع داده های بومی از لینوکس یا ویندوز استفاده کنید. ما DLLمی تواند توسط برنامه نویس دیگری که از زبان برنامه نویسی متفاوتی استفاده می کند استفاده شود.

می تواند به کار رود رشته هایو آرایه های پویا v DLLدر دلفی نوشته شده است، اما برای این کار باید ماژول را وصل کنید ShareMemبه بخش استفاده می کند v DLLو برنامه ای که از آن استفاده خواهد کرد. ضمناً این آگهی باید اولین قسمت باشد استفاده می کندهر فایل پروژه

انواع رشته، همانطور که می دانیم ، C ، C ++ و سایر زبانها وجود ندارد ، بنابراین توصیه می شود به جای آنها استفاده کنید PChar.

DLL نمونه

کتابخانه Example_dll; از SysUtils، Classes استفاده می کند. var (اعلان متغیرها) k1: عدد صحیح; k2: عدد صحیح؛ (ما تابع را اعلام می کنیم) تابع SummaTotal (ضریب: عدد صحیح): عدد صحیح; ثبت نام؛ فاکتور شروع: = فاکتور * k1 + ضریب; فاکتور: = فاکتور * k2 + ضریب; نتیجه: = فاکتور; پایان؛ (ما تابع را برای استفاده بیشتر توسط برنامه صادر می کنیم) SummaTotal صادر می کند. (مقداردهی اولیه) شروع k1: = 2؛ k2: = 5; پایان.

نمونه ای از فراخوانی یک تابع از یک DLL

واحد واحد 1; رابط از Windows ، Messages ، SysUtils ، Variants ، Classs ، Graphics ، Controls ، Forms ، Dialogs ، StdCtrls استفاده می کند. نوع TForm1 = کلاس (TForm) Button1: TButton; رویه Button1Click (فرستنده: TObject); private (Private declarations) public (Public declarations) end; نوع TSummaTotal = تابع (ضریب: عدد صحیح): عدد صحیح; var Form1: TForm1; پیاده سازی ($ R * .dfm) رویه TForm1.Button1Click (فرستنده: TObject); var dll_instance: Thandle; SummaTotal: TSummaTotal; begin dll_instance: = LoadLibrary ("Example_dll.dll")؛ @SummaTotal: = GetProcAddress (dll_instance، "SummaTotal")؛ Label1.Caption: = IntToStr (SummaTotal (5)); FreeLibrary (dll_instance)؛ پایان؛ پایان.

زنگ

کسانی هستند که قبل از شما این خبر را می خوانند.
برای دریافت آخرین مقالات مشترک شوید.
پست الکترونیک
نام
نام خانوادگی
چگونه می خواهید زنگ را بخوانید
بدون هرزنامه