زنگ

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

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

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

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

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

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

// \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d // .def پرونده LIBRARY myfirstdll.dll توضیحات توضیحات "My DLL اول من" EXPORTS MyF // //\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d // هدر DLL که درج می شود BoF کد سرویس گیرنده MyFunction (int parms)؛ // \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d // اجرای عملکرد در DLL bool MyFunction (int parms) (// انجام همه چیزهایی که ما نیاز داریم ............)

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

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

در حین تدوین DLL ، کلید MYFIRSTDLL_EXPORTS تعریف شده است ، بنابراین کلمه کلیدی __decl Spec (dllexport) قبل از اعلامیه های اشیاء صادر شده جایگزین می شود. و وقتی کد مشتری کامپایل می شود ، این کلید تعریف نشده است و پیشوند __decl Spec (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 استفاده کنید ، می توانید به سادگی از پیشوند __decl Spec (dllexport) قبل از توابع صادر شده استفاده کنید. هر دو روش همان کار را انجام می دهند. مشتری DLL را با انتقال نام پرونده DLL به تابع Win32 LoadLibrary () بار می دهد.این عملکرد عملکرد دسته HINSTANCE را که برای کار با DLL استفاده می شود ، باز می گرداند و در هنگام نیاز به خالی کردن DLL از حافظه ضروری است. پس از بارگذاری DLL ، مشتری می تواند با استفاده از نام عملکرد مورد نیاز به عنوان پارامتر ، با استفاده از تابع GetProcAddress () از هر عملکردی استفاده کند.

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

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

// \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d // //def پرونده LIBRARY myinterface.dll DESCRIPTION "پیاده سازی رابط I_MyInterface EXPORTS GetMyInterface FreeMyInterface // \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d // فایل هدر مورد استفاده در Dll و به مشتری // که رابط را اعلام می کند // I_MyInterface.h ساختار I_MyInterface (Init bool Init (int parms) \u003d 0؛ انتشار bool مجازی () \u003d 0؛ DoStuff باطل مجازی () \u003d 0؛)؛ / * اعلامیه های صادر شده Dll و تعاریف انواع نشانگرهای عملکرد برای بارگذاری آسان و کار با توابع: به پیشوند بیرونی "C" توجه کنید ، که به کامپایلر می گوید که از توابع C-style * / extern "C" استفاده می شود (HRESULT GetMyInterface (I_MyInterface ** pInterface)؛ typedef HRESULT (* GETINTERFACE) (I_MyInterface ** pInterface)؛ HRESULT FreeMyInterface (I_MyInterface ** pInterface)؛ typedef HRESULT (* FREEINTERFACE) (I_MyInterface ** pInterface)؛) // \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d // // // پیاده سازی رابط کاربری در کلاس Dll // MyInterface.h کلاس CMyClass: عمومی I_MyInterface (عمومی: bool Init (int parms)؛ bool انتشار ()؛ DoStuff خالی است ()؛ CMyClass ()؛ CMyClass ()؛ // سایر اعضای کلاس ............ خصوصی: // اعضای عضو کلاس ............)؛ // \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d // توابع صادر شده که رابط را ایجاد کرده و از بین می برد // Dllmain.h HRESULT GetMyInterface (I_MyInterface ** pInterface) (اگر (! * PInterface) (* pInterface \u003d CMyClass جدید؛ بازگشت بازگشت S_OK؛) بازگشت E_FAIL؛) HRESULT FreeMyInterface (I_MyInterface ** pInterface) (اگر (! * PInterface) E_FAIL را برگرداند ؛ حذف * pInterface ؛ * pInterface \u003d 0؛ S_OK را برگردانید) // \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d // کد مشتری // اعلانات رابط و تماس های عملکردی GETINTERFACE pfnInterface \u003d 0؛ // اشاره گر به تابع GetMyInterface I_MyInterface * pInterface \u003d 0؛ // اشاره گر به ساختار MyInterface HINSTANCE hMyDll \u003d :: LoadLibrary ("myinterface.dll")؛ if (hMyDll! \u003d NULL) (// تعیین آدرس عملکرد pfnInterface \u003d (GETINTERFACE) :: GetProcAddress (hMyDll ، "GetMyInterface") ؛ // بارگیری DLL در صورت عدم موفقیت در عملکرد قبلی اگر (pfnInterface \u003d\u003d 0) (:: FreeLibrary (hMyDll)؛ بازگشت؛) // با عملکرد HRESULT تماس بگیرید hr \u003d pfnInterface (& pInterface)؛ // بارگیری اگر ناموفق باشد اگر (FAILED (hr)) (:: FreeLibrary (hMyDll)؛ Return؛) // رابط بارگیری شد ، می توانید تماس بگیرید توابع pInterface-\u003e Init (1)؛ pInterface-\u003e DoStuff ()؛ pInterface-\u003e Release () // // رابط رایگان رايگان pfnFree \u003d (FREEINTERFACE) :: GetProcAddress (hMyDll، "FreeMyInterface")؛ if (pfnFree!) \u003d 0) pfnFree (& hMyDll)؛ // بارگیری DLL :: FreeLibrary (hMyDll)؛ )

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

از بدو تولد (یا اندکی دیرتر) ، سیستم عامل ویندوز از کتابخانه های لینک پویا (کتابخانه پیوند دینامیک) استفاده کرد ، که شامل پیاده سازی های متداول ترین توابع بود. وراث ویندوز - NT و ویندوز 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 از بین سایر پارامترهای ویرایشگر پیوند در خط فرمان یا در تب لینک در کادر محاوره ای Project Settings از Developer Studio مشخص می شود. با این حال ، پرونده .lib استفاده شده است هنگام ضمنی اتصال DLL ، - این یک کتابخانه معمولی استاتیک نیست. چنین پرونده های .lib نامیده می شوند وارد کردن کتابخانه ها (کتابخانه های واردات). آنها حاوی كتاب كتابخانه نیستند ، بلكه فقط به تمام توابع صادر شده از پرونده DLL كه در آن همه چیز ذخیره می شود ، پیوند دارند. در نتیجه ، کتابخانه های واردات نسبت به پرونده های DLL کوچکتر هستند. بعداً به روشهای ایجاد آنها باز خواهیم گشت. حال بیایید موضوعات دیگری راجع به ارتباط ضمنی کتابخانه های پویا بررسی کنیم.

مذاکره رابط

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

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

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

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

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

External "C" int MyOldCFunction (int myParam)؛

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

بیرونی "C" (# شامل "MyCLib.h")

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

DLL را ضمیمه کنید

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

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

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

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

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

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

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

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

HINSTANCE hMyDll؛ :: if ((hMyDll \u003d :: LoadLibrary ("MyDLL")) \u003d\u003d NULL) (/ * DLL بارگیری نشد * /) دیگر (/ * برنامه حق استفاده از توابع DLL را از طریق hMyDll * /) دارد

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

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

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

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

// نوع PFN_MyFunction یک اشاره گر را به یک تابع اعلام می کند // پذیرش یک اشاره گر به یک بافر کاراکتر و بازگرداندن مقداری از نوع int int typedef int (WINAPI * PFN_MyFunction) (char *)؛ :: PFN_MyFunction pfnMyFunction؛

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

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

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

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

PfnMyFunction \u003d (PFN_MyFunction) :: GetProcAddress (hMyDll ، MAKEINTRESOURCE (1))؛

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

:: FreeLibrary (hMyDll)؛

برنامه های افزودنی MFC Library Dynamic Library را بارگیری کنید

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

منابع DLL

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

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

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

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

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

mydll.h صادرات #define خارجی "C" __decl Spec (dllexport) EXPORT int CALLBACK MyFunction (char * str)؛

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

MyDLL.c #include #inc "MyDLL.h" int WINAPI DllMain (HISTANCE hInstance، DWORD fdReason، PVOID pvReservation) (Return TRUE؛) EXPORT int CALLBACK MyFunction (char * str) (MessageBox (NULL، str، "Function from DLL"، MB_OK)؛ بازگشت 1؛)

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

به عنوان مثال اتصال DLL ضمنی توسط برنامه

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

#عبارتند از #inc "MyDLL.h" را وارد کنید WINAPI WinMain (HINSTANCE hInstance، HINSTANCE hPrevInstance، LPSTR lpCmdLine، int nCmdShow) (int iCode \u003d MyFunction ("سلام")؛ بازگشت 0؛)

این برنامه به نظر می رسد برنامه های منظم برای ویندوز ، که در اصل این است. با این وجود ، لازم به ذکر است که علاوه بر فراخوانی عملکرد MyFunction از DLL ، فایل هدر این کتابخانه 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 توسط یک برنامه

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

#عبارتند از typedef int (WINAPI * PFN_MyFunction) (char *)؛ int WINAPI WinMain (HINSTANCE hInstance، HINSTANCE hPrevInstance، LPSTR lpCmdLine، int nCmdShow) (HINSTANCE hMyDll؛ اگر ((hMyDll \u003d LoadLibrary ("MyDLL")) \u003d\u003d NFL عملکرد ، "MyFunction")؛ int iCode \u003d (* pfnMyFunction) ("سلام")؛ FreeLibrary (hMyDll)؛ 0 را برگردانید.)

ایجاد DLL

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

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

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

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

عملکرد Dllmain

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

BOOL WINAPI DllMain (HANDLE hInst، DWORD dwReason، LPVOID IpRes محافظت شده) (BOOL bAllWentWell \u003d TRUE؛ سوئیچ (dwReason) (مورد DLL_PROCESS_ATTACH: // اولویت دادن به فرآیند.break؛ پرونده DLL_THREAD_ATTACH: // پرونده ساختارهای جریان. break (پرونده) DLL_PROCESS_DETACH: // ساختارهای فرآیند را پاک کنید.

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

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

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

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

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

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

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

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

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

روش __Declspec (dllexport)

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

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

پرونده های تعریف ماژول

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

mydll.def شرح کتابخانه "MyDLL" شرح "MyDLL - مثال DLL" EXPORTS Myfunction @ 1

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

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

MyFunction @ 1 نامگذاری نشده است

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

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

صادرات #define خارجی "C" __decl Spec (dllexport) EXPORT int CALLBACK MyFunction (char * str)؛ مانند این: extern "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 نظر (lib، "msvcrt" "-بخش: :mymyg، rws")؛

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

تدوین کامل DLL

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

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

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

DLL و MFC

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

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

معمولی MFC DLL

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

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

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

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

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

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

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

AFX_MANAGE_STATE (AfxGetStaticModuleState ())؛

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

افزونه های پویا MFC

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

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

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

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

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

استاتیک AFX_EXTENSION_MODULE MyExtDLL \u003d (NULL، NULL)؛ extern "C" int APIENTRY DllMain (قطعنامه HINSTANCE ، DWORD dwReason ، LPVOID IpRes محافظت شده) (if (dwReason \u003d\u003d DLL_PROCESS_ATTACH) (TRACED ("MYEXT.DLL Initialization! \\ n")؛ // Extension DLL یکبار شروع اولیه ، مطمئناً ؛ // // قرار دادن این DLL در زنجیره منبع CDynLinkLibrary جدید (MyExtDLL)؛) دیگری اگر (dwReason \u003d\u003d DLL_PROCESS_DETACH) (TRACED ("MYEXT.DLL خاتمه! \\ n")))) 1 (// ok)

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

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

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

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

اگر (dwReason \u003d\u003d DLL_PROCESS_DETACH) (AfxTermExtensionModule (MyExtDLL)؛)

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

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

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

کلاس AFX_EXT_CLASS CMyClass: عمومی CObject (// اعلامیه کلاس شما) باطل AFX_EXT_API MyFunc ()؛

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

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

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

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

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

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

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

الف) ضمنی؛

ج) معوق

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

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

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

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

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

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

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

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

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

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

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

در صورت بار صریح ، روند کار با DLL در سه مرحله انجام می شود:

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

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

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

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

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

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

توابع می توانند هشداری را در اختیار کاربر قرار دهند.

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

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

عملکرد ویژه ای را در ماژول EXE اجرا کنید
تأخیرLoadHelper؛

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

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

تماس های عملکرد را از DLL به تماس تبدیل کنید
تأخیر

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

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

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

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

ایجاد DLL

در منو فایل جدید -\u003e موارد دیگر را انتخاب کنید. در کادر گفتگو ، تب جدید انتخاب کنید جادوگر DLL. یک ماژول به طور خودکار ایجاد می شود - الگوی خالی برای آینده Dll.

نحو DLL

ساختن Dllانتخاب کنید پروژه -\u003e ساخت نام پروژه .

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

عملکردها و رویه ها را می توان محلی و از آنجا صادر کرد Dll.

محلی

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

صادر شده است

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

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

بارگیری DLL

دو نوع چکمه در دلفی وجود دارد Dll:

بار ثابت

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

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

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

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

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

اعلامیه نوع عملکرد

نوع TSumaTotal \u003d تابع (عامل: عدد صحیح): عدد صحیح؛

بارگیری کتابخانه

dll_instance: \u003d LoadLibrary ("مثال_dll.dll")؛

ما به یک تابع اشاره گر می گیریم

SummaTotal: \u003d GetProcAddress (dll_instance ، "SummaTotal")؛

تماس با عملکرد

Label1.Caption: \u003d IntToStr (SummaTotal (5))؛

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

FreeLibrary (dll_instance)؛

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

صادرات ردیف

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

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

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

به عنوان مثال DLL

کتابخانه مثال_dll؛ از SysUtils ، Class ها استفاده می کند. var (متغیرهای اعلام شده) k1: عدد صحیح؛ k2: عدد صحیح؛ (اعلام یک تابع) تابع SummaTotal (فاکتور: عدد صحیح): عدد صحیح؛ ثبت نام؛ عامل شروع: \u003d عامل * k1 + عامل؛ عامل: \u003d عامل * K2 + فاکتور؛ نتیجه: \u003d عامل؛ پایان؛ (ما عملکرد را برای استفاده بیشتر توسط برنامه صادر می کنیم) SummaTotal را صادر می کنیم. (شروع متغیرها) k1 شروع می شود: \u003d 2؛ k2: \u003d 5؛ پایان.

نمونه ای از تماس تابع از DLL

واحد واحد 1؛ رابط با استفاده از ویندوز ، پیام ها ، SysUtils ، انواع مختلف ، کلاس ها ، گرافیک ، کنترل ها ، فرم ها ، دیالوگ ها ، StdCtrls. نوع TForm1 \u003d کلاس (TForm) دکمه1: TButton؛ Procedure Button1Click (فرستنده: TObject)؛ اعلامیه های خصوصی (اعلامیه های خصوصی) عمومی (اعلامیه های عمومی) پایان می یابد. نوع TSummaTotal \u003d تابع (عامل: عدد صحیح): عدد صحیح؛ var Form1: TForm1؛ پیاده سازی ($ R * .dfm) روش TForm1.Button1 کلیک (ارسال کننده: موضوع)؛ var dll_instance: Thandle؛ SummaTotal: TSummaTotal؛ dll_instance را شروع کنید: \u003d LoadLibrary ("مثال_dll.dll")؛ SummaTotal: \u003d GetProcAddress (dll_instance ، "SummaTotal")؛ Label1.Caption: \u003d IntToStr (SummaTotal (5))؛ FreeLibrary (dll_instance)؛ پایان؛ پایان.

زنگ

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