Mga function ng pagtawag mula sa mga DLL. Paglikha at Paggamit ng mga DLL sa Delphi Full DLL Compilation

Mula sa pagsilang nito (o ilang sandali), ang Windows operating system ay gumamit ng mga DLL (Dynamic Link Libraries), na naglalaman ng mga pagpapatupad ng mga pinakakaraniwang ginagamit na function. Ang mga kahalili ng Windows - NT at Windows 95, pati na rin ang OS/2 - ay nakasalalay din sa mga DLL para sa karamihan ng kanilang pag-andar.

Tingnan natin ang ilang aspeto ng paglikha at paggamit ng mga DLL:

  • kung paano i-static na i-link ang mga DLL;
  • kung paano dynamic na i-load ang mga DLL;
  • kung paano lumikha ng mga DLL;
  • paano gumawa ng mga extension ng MFC DLL.

Gamit ang mga DLL

Ito ay halos imposible upang lumikha ng isang Windows application na hindi gumagamit ng mga DLL. Ang DLL ay naglalaman ng lahat ng mga function ng Win32 API at hindi mabilang na iba pang mga function ng Win32 operating system.

Sa pangkalahatan, ang mga DLL ay mga koleksyon lamang ng mga function na nakolekta sa mga aklatan. Gayunpaman, hindi katulad ng kanilang mga static na pinsan (.lib file), ang mga DLL ay hindi direktang naka-link sa mga executable gamit ang isang linker. Ang executable file ay naglalaman lamang ng impormasyon tungkol sa kanilang lokasyon. Sa oras ng pagpapatupad ng programa, ang buong library ay na-load. Nagbibigay-daan ito sa iba't ibang proseso na magbahagi ng parehong mga aklatan sa memorya. Binibigyang-daan ka ng diskarteng ito na bawasan ang memorya na kinakailangan para sa maraming application na gumagamit ng maraming shared library, pati na rin kontrolin ang laki ng mga EXE file.

Gayunpaman, kung ang aklatan ay ginagamit lamang ng isang application, ito ay mas mahusay na gawin itong isang regular, static na isa. Siyempre, kung ang mga function na kasama dito ay gagamitin lamang sa isang programa, maaari mo lamang ipasok ang kaukulang source text file dito.

Kadalasan, ang isang proyekto ay nakakonekta sa isang DLL nang statically, o implicitly, sa oras ng link. Ang paglo-load ng mga DLL sa panahon ng pagpapatupad ng programa ay kinokontrol ng operating system. Gayunpaman, maaaring i-load ang mga DLL nang tahasan o pabago-bago habang tumatakbo ang application.

Mag-import ng mga Aklatan

Kapag nagli-link ng DLL nang statically, tinutukoy ang pangalan ng .lib file kasama ng iba pang opsyon sa Link Editor sa command line o sa tab na Link ng dialog box ng Project Settings sa Developer Studio. Gayunpaman, ginamit ang .lib file kapag tahasan ang pagkonekta ng isang DLL,- hindi ito isang ordinaryong static na library. Ang mga nasabing .lib file ay tinatawag mag-import ng mga aklatan(mag-import ng mga aklatan). Hindi naglalaman ang mga ito ng library code mismo, ngunit nagli-link lamang sa lahat ng mga function na na-export mula sa DLL file kung saan nakaimbak ang lahat. Bilang resulta, ang mga aklatan sa pag-import ay malamang na mas maliit ang laki kaysa sa mga DLL file. Babalik tayo sa kung paano gawin ang mga ito sa ibang pagkakataon. Ngayon tingnan natin ang iba pang mga isyu na nauugnay sa implicit na pagsasama ng mga dynamic na aklatan.

Interface negosasyon

Kapag gumagamit ng iyong sarili o mga third-party na aklatan, kailangan mong bigyang pansin ang pagtutugma ng function call sa prototype nito.

Kung perpekto ang mundo, hindi na kailangang mag-alala ang mga programmer tungkol sa pagtutugma ng mga interface ng function kapag kumokonekta sa mga aklatan - pareho silang lahat. Gayunpaman, ang mundo ay malayo sa perpekto, at maraming malalaking programa ang isinulat gamit ang iba't ibang mga aklatan na walang C++.

Bilang default, ang Visual C++ ay sumusunod sa mga interface ng function ayon sa mga panuntunan ng C++. Nangangahulugan ito na ang mga parameter ay itinutulak papunta sa stack mula kanan pakaliwa, at ang tumatawag ay may pananagutan sa pag-alis ng mga ito mula sa stack kapag lumabas sa function at pagpapalawak ng pangalan nito. Ang name mangling ay nagbibigay-daan sa linker na makilala sa pagitan ng mga overloaded na function, i.e. mga function na may parehong mga pangalan ngunit iba't ibang mga listahan ng mga argumento. Gayunpaman, ang lumang C library ay walang pinalawak na pinangalanang function.

Bagama't ang lahat ng iba pang mga panuntunan para sa pagtawag ng isang function sa C ay magkapareho sa mga patakaran para sa pagtawag ng isang function sa C++, ang mga pangalan ng function ay hindi pinalawak sa mga aklatan ng C. Ang mga ito ay pinangungunahan lamang ng isang underscore (_).

Kung kailangan mong ikonekta ang isang C library sa isang C++ application, ang lahat ng mga function mula sa library na ito ay kailangang ideklara bilang panlabas sa C format:

Extern "C" int MyOldCFunction(int myParam);

Ang mga deklarasyon ng mga function ng library ay karaniwang inilalagay sa header file ng library, bagama't ang mga header ng karamihan sa mga C library ay hindi idinisenyo para gamitin sa mga proyektong C++. Sa kasong ito, dapat kang lumikha ng isang kopya ng header file at isama ang extern "C" modifier sa deklarasyon ng lahat ng mga function ng library na ginamit. Ang panlabas na "C" modifier ay maaari ding ilapat sa isang buong bloke, kung saan nakakonekta ang lumang C header file gamit ang #tinclude na direktiba. Kaya, sa halip na baguhin ang bawat function nang hiwalay, maaari kang makayanan gamit ang tatlong linya lamang:

Extern na "C" ( #include "MyCLib.h")

Gumamit din ang mga program para sa mga mas lumang bersyon ng Windows ng PASCAL function calling conventions para sa Windows API functions. Dapat gamitin ng mga bagong programa ang winapi modifier, na nagko-convert sa _stdcall. Bagama't hindi ito isang karaniwang interface ng function na C o C++, ito ang ginagamit upang tawagan ang mga function ng Windows API. Gayunpaman, kadalasan ang lahat ng ito ay isinasaalang-alang na sa karaniwang mga header ng Windows.

Naglo-load ng Implicit DLL

Kapag nagsimula ang isang application, sinusubukan nitong hanapin ang lahat ng DLL file na tahasang naka-attach sa application at ilagay ang mga ito sa lugar ng RAM na inookupahan ng proseso. Ang operating system ay naghahanap ng mga DLL file sa sumusunod na pagkakasunod-sunod.

  • Ang direktoryo kung saan matatagpuan ang EXE file.
  • Ang kasalukuyang direktoryo ng proseso.
  • Direktoryo ng system ng Windows.

Kung ang DLL ay hindi natagpuan, ang application ay nagpapakita ng isang dialog box na nagpapahiwatig ng kawalan nito at ang mga landas na hinanap. Pagkatapos ay magsasara ang proseso.

Kung ang kinakailangang library ay natagpuan, ito ay inilalagay sa RAM ng proseso, kung saan ito ay nananatili hanggang sa katapusan ng proseso. Maa-access na ngayon ng application ang mga function na nakapaloob sa DLL.

Dynamic na pag-load at pag-unload ng mga DLL

Sa halip na magkaroon ng dynamic na link ng Windows sa DLL kapag unang na-load ang application sa memorya, maaari mong i-link ang program sa module ng library sa runtime (sa pamamaraang ito, hindi mo kailangang gamitin ang import library kapag gumagawa ng application). Sa partikular, matutukoy mo kung aling mga DLL ang magagamit ng user, o payagan ang user na pumili kung aling mga DLL ang ilo-load. Sa ganitong paraan maaari kang gumamit ng iba't ibang mga DLL na nagpapatupad ng parehong mga function upang magsagawa ng iba't ibang mga aksyon. Halimbawa, ang isang application na idinisenyo para sa independiyenteng paglilipat ng data ay makakapagdesisyon sa runtime kung maglo-load ng DLL para sa TCP/IP protocol o para sa isa pang protocol.

Naglo-load ng isang regular na DLL

Ang unang bagay na kailangan mong gawin kapag dynamic na naglo-load ng isang DLL ay ilagay ang module ng library sa memorya ng proseso. Ginagawa ang operasyong ito gamit ang function ::LoadLibrary, na mayroong isang argumento - ang pangalan ng na-load na module. Ang katumbas na fragment ng programa ay dapat magmukhang ganito:

HINSTANCE hMyDll; :: if((hMyDll=:: LoadLibrary("MyDLL"))==NULL) ( /* nabigong i-load ang DLL */ ) iba pa ( /* pinapayagan ang application na gumamit ng mga function ng DLL sa pamamagitan ng hMyDll */ )

Itinuturing ng Windows na ang karaniwang extension ng file ng library ay .dll maliban kung tumukoy ka ng ibang extension. Kung may kasamang path din ang pangalan ng file, ang path lang na iyon ang gagamitin upang maghanap para sa file. Kung hindi, hahanapin ng Windows ang file sa parehong paraan tulad ng para sa implicitly na kasamang mga DLL, simula sa direktoryo kung saan na-load ang exe file at nagpapatuloy ayon sa halaga ng PATH.

Kapag nahanap ng Windows ang file, ang buong landas nito ay ihahambing sa landas ng mga DLL na na-load na ng proseso. Kung may nakitang pagkakakilanlan, sa halip na mag-download ng kopya ng application, ibabalik ang isang handle sa nakasama nang library.

Kung natagpuan ang file at matagumpay na na-load ang library, ang function ::LoadLibrary ibinabalik ang hawakan nito, na ginagamit upang ma-access ang mga function ng library.

Bago gamitin ang mga function ng library, dapat mong makuha ang kanilang address. Upang gawin ito, kailangan mo munang gamitin ang direktiba typedef upang tukuyin ang uri ng isang function pointer at tukuyin ang isang variable ng bagong uri na ito, halimbawa:

// ang uri ng PFN_MyFunction ay magdedeklara ng pointer sa isang function // na kumukuha ng pointer sa isang character buffer at gumagawa ng halaga ng uri int typedef int (WINAPI *PFN_MyFunction)(char *); ::PFN_MyFunction pfnMyFunction;

Pagkatapos ay dapat kang makakuha ng deskriptor ng library, sa tulong kung saan matutukoy mo ang mga address ng mga function, halimbawa, ang address ng isang function na pinangalanang MyFunction:

HMyDll=::LoadLibrary("MyDLL"); pfnMyFunction=(PFN_MyFunction)::GetProcAddress(hMyDll,"MyFunction"); :: int iCode=(*pfnMyFunction)("Hello");

Tinutukoy ang address ng function gamit ang function ::GetProcAddress, dapat itong ipasa ang pangalan ng library at ang pangalan ng function. Ang huli ay dapat ipadala sa anyo kung saan ito ay na-export mula sa DLL.

Maaari ka ring sumangguni sa isang function sa pamamagitan ng ordinal na numero kung saan ito na-export (sa kasong ito, isang def file ay dapat gamitin upang lumikha ng library, ito ay tatalakayin sa ibang pagkakataon):

PfnMyFunction=(PFN_MyFunction)::GetProcAddress(hMyDll, MAKEINTRESOURCE(1));

Pagkatapos ng pagtatrabaho sa dynamic link library, maaari itong i-unload mula sa memorya ng proseso gamit ang function na:: LibrengLibrary:

::FreeLibrary(hMyDll);

Naglo-load ng mga extension ng MFC sa mga dynamic na library

Kapag naglo-load ng mga extension ng MFC para sa mga DLL (tinalakay nang detalyado sa ibaba) sa halip na mga function LoadLibrary At LibrengLibrary ginagamit ang mga function AfxLoadLibrary At AfxFreeLibrary. Ang huli ay halos magkapareho sa mga function ng Win32 API. Tinitiyak lamang nila na ang mga istruktura ng MFC na sinimulan ng extension ng DLL ay hindi napinsala ng ibang mga thread.

Mga Mapagkukunan ng DLL

Nalalapat din ang dynamic na pag-load sa mga mapagkukunan ng DLL na ginagamit ng MFC upang i-load ang mga karaniwang mapagkukunan ng application. Upang gawin ito, kailangan mo munang tawagan ang function LoadLibrary at ilagay ang DLL sa memorya. Pagkatapos ay ginagamit ang function AfxSetResourceHandle kailangan mong ihanda ang window ng programa upang makatanggap ng mga mapagkukunan mula sa bagong-load na library. Kung hindi, ilo-load ang mga mapagkukunan mula sa mga file na naka-attach sa executable file ng proseso. Maginhawa ang diskarteng ito kung kailangan mong gumamit ng iba't ibang hanay ng mga mapagkukunan, halimbawa para sa iba't ibang wika.

Magkomento. Gamit ang function LoadLibrary Maaari mo ring i-load ang mga maipapatupad na file sa memorya (huwag ilunsad ang mga ito para sa pagpapatupad!). Ang executable module handle ay maaaring gamitin kapag tumatawag sa mga function FindResource At LoadResource upang maghanap at mag-download ng mga mapagkukunan ng application. Ang mga module ay dini-load din mula sa memorya gamit ang function LibrengLibrary.

Halimbawa ng isang karaniwang DLL at mga paraan ng paglo-load

Narito ang source code ng isang dynamic na link library na tinatawag na MyDLL at naglalaman ng isang function na MyFunction, na nagpapakita lamang ng isang mensahe.

Una, ang isang macro constant ay tinukoy sa header file EXPORT. Ang paggamit ng keyword na ito kapag tinutukoy ang isang function sa isang dynamic na link library ay nagsasabi sa linker na ang function ay magagamit para sa paggamit ng iba pang mga program, na nagiging sanhi upang maisama ito sa import library. Bilang karagdagan, ang naturang function, tulad ng isang window procedure, ay dapat tukuyin gamit ang pare-pareho CALLBACK:

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

Ang file ng library ay medyo naiiba din sa mga regular na C file para sa Windows. Naglalaman ito sa halip na isang function WinMain may function DllMain. Ginagamit ang function na ito para magsagawa ng initialization, na tatalakayin sa ibang pagkakataon. Upang ang library ay manatili sa memorya pagkatapos itong ma-load upang ang mga function nito ay matawagan, ang return value nito ay dapat na TOTOO:

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,str,"Function from DLL",MB_OK); balik 1;)

Pagkatapos isalin at i-link ang mga file na ito, lalabas ang dalawang file - MyDLL.dll (ang mismong dynamic na link library) at MyDLL.lib (import library nito).

Halimbawa ng implicit DLL na koneksyon sa pamamagitan ng application

Ipakita natin ngayon ang source code ng isang simpleng application na gumagamit ng MyFunction function mula sa MyDLL.dll library:

#isama #include "MyDLL.h" int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ( int iCode=MyFunction("Hello"); return 0; )

Ang program na ito ay mukhang isang regular na programa sa Windows, na kung ano mismo ito. Gayunpaman, dapat tandaan na sa source text nito, bilang karagdagan sa pagtawag sa MyFunction function mula sa DLL library, kasama rin ang header file ng library na ito, MyDLL.h. Kinakailangan din na kumonekta dito sa yugto ng komposisyon ng aplikasyon import library MyDLL.lib (ang proseso ng tahasang pagkonekta ng isang DLL sa isang executable na module).

Napakahalagang maunawaan na ang MyFunction code mismo ay hindi kasama sa MyApp.exe file. Sa halip, naglalaman lang ito ng link sa MyDLL.dll file at link sa MyFunction function na nasa file na iyon. Ang MyApp.exe file ay nangangailangan ng MyDLL.dll file upang tumakbo.

Ang MyDLL.h header file ay kasama sa MyApp.c program source file sa parehong paraan na ang windows.h file ay kasama doon. Ang pagsasama ng MyDLL.lib import library para sa pag-link ay kapareho ng pagsasama ng lahat ng Windows import library doon. Kapag tumatakbo ang MyApp.exe, nagli-link ito sa MyDLL.dll sa parehong paraan tulad ng lahat ng karaniwang Windows dynamic na link library.

Halimbawa ng dynamic na paglo-load ng isang DLL ng isang application

Ipakita natin ngayon ang kumpletong source code ng isang simpleng application na gumagamit ng MyFunction function mula sa MyDLL.dll library, gamit ang dynamic na pag-load ng library:

#isama typedef int (WINAPI *PFN_MyFunction)(char *); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ( HINSTANCE hMyDll; if((hMyDll=LoadLibrary("MyDLL"))==NULL) return 1; PFN_MyFunctionFunctionFnFunctionFnFunction; cAddress(hMyDll ,"MyFunction"); int iCode=(*pfnMyFunction)("Hello"); FreeLibrary(hMyDll); return 0; )

Paglikha ng isang DLL

Ngayon, na naging pamilyar sa mga prinsipyo ng pagpapatakbo ng mga DLL sa mga application, tingnan natin kung paano likhain ang mga ito. Kapag bumubuo ng isang application, ipinapayong ilagay ang mga function na na-access ng ilang mga proseso sa isang DLL. Nagbibigay-daan ito para sa mas mahusay na paggamit ng memorya sa Windows.

Ang pinakamadaling paraan upang lumikha ng bagong proyekto ng DLL ay ang paggamit ng AppWizard wizard, na awtomatikong nagsasagawa ng marami sa mga operasyon. Para sa mga simpleng DLL, tulad ng mga tinalakay sa kabanatang ito, dapat mong piliin ang uri ng proyekto ng Win32 Dynamic-Link Library. Ang bagong proyekto ay bibigyan ng lahat ng kinakailangang mga parameter upang lumikha ng DLL. Ang mga source file ay kailangang manu-manong idagdag sa proyekto.

Kung plano mong samantalahin nang husto ang functionality ng MFC, gaya ng mga dokumento at view, o nagnanais na lumikha ng OLE automation server, mas mabuting piliin ang uri ng proyekto ng MFC AppWizard (dll). Sa kasong ito, bilang karagdagan sa pagtatalaga ng mga parameter sa proyekto para sa pagkonekta ng mga dynamic na aklatan, ang wizard ay gagawa ng ilang karagdagang trabaho. Ang mga kinakailangang link sa mga library ng MFC at mga source file na naglalaman ng paglalarawan at pagpapatupad sa DLL ng isang object class ng application na hinango mula sa CWinApp.

Minsan ito ay maginhawa upang unang lumikha ng isang proyekto tulad ng MFC AppWizard (dll) bilang isang pagsubok na application, at pagkatapos ay lumikha ng DLL bilang bahagi nito. Bilang resulta, awtomatikong malilikha ang DLL kung kinakailangan.

DllMain function

Karamihan sa mga DLL ay mga koleksyon lamang ng mahalagang independiyenteng mga function na na-export sa at ginagamit ng mga application. Bilang karagdagan sa mga function na inilaan para sa pag-export, ang bawat DLL ay may isang function DllMain. Ang function na ito ay idinisenyo upang simulan at linisin ang isang DLL. Pinalitan nito ang mga function LibMain At WEP, na ginamit sa mga nakaraang bersyon ng Windows. Istraktura ng pinakasimpleng function DllMain maaaring ganito ang hitsura, halimbawa:

BOOL WINAPI DllMain (HANDLE hInst,DWORD dwReason, LPVOID IpReserved) ( BOOL bAllWentWell=TRUE; switch (dwReason) ( case DLL_PROCESS_ATTACH: // Process initialization. break; case DLL_THREAD_ATTACH: // Cleaning DLLDETACH_break: // Cleaning DLLDETACH: // Pag-init ng thread. thread structures. break; case DLL_PROCESS_DETACH: // Pag-clear ng mga istruktura ng proseso. break; ) if(bAllWentWell) return TRUE; else return FALSE; )

Function DllMain tinawag sa ilang mga kaso. Ang dahilan ng pagtawag dito ay tinutukoy ng dwReason parameter, na maaaring tumagal ng isa sa mga sumusunod na value.

Kapag ang isang DLL ay unang na-load ng isang proseso, ang function ay tinatawag DllMain na may dwReason na katumbas ng DLL_PROCESS_ATTACH. Sa bawat oras na ang isang proseso ay lumilikha ng isang bagong thread, ang DllMainO ay tinatawag na may dwReason katumbas ng DLL_THREAD_ATTACH (maliban sa unang thread, dahil sa kasong ito dwReason ay katumbas ng DLL_PROCESS_ATTACH).

Matapos ang proseso ay tapos na magtrabaho kasama ang DLL, ang function DllMain tinatawag na may dwReason parameter na katumbas ng DLL_PROCESS_DETACH. Kapag nasira ang isang thread (maliban sa una), ang dwReason ay magiging katumbas ng DLL_THREAD_DETACH.

Ang lahat ng pagpapatakbo ng pagsisimula at paglilinis para sa mga proseso at thread na kailangan ng DLL ay dapat gawin batay sa halaga ng dwReason, tulad ng ipinapakita sa nakaraang halimbawa. Karaniwang limitado ang pagsisimula ng proseso sa paglalaan ng mga mapagkukunang ibinabahagi sa pagitan ng mga thread, tulad ng pag-load ng mga nakabahaging file at pagsisimula ng mga library. Ginagamit ang pagsisimula ng thread upang i-configure ang mga mode na partikular lamang sa isang naibigay na thread, halimbawa, upang simulan ang lokal na memorya.

Ang isang DLL ay maaaring maglaman ng mga mapagkukunan na hindi pagmamay-ari ng application na tumatawag dito. Kung gumagana ang mga function ng DLL sa mga mapagkukunan ng DLL, malinaw na magiging kapaki-pakinabang ang pag-imbak ng isang hInst handle sa isang lugar sa labas ng paraan at gamitin ito kapag naglo-load ng mga mapagkukunan mula sa DLL. Ang IpReserved pointer ay nakalaan para sa panloob na paggamit ng Windows. Samakatuwid, hindi ito dapat i-claim ng aplikasyon. Maaari mo lamang suriin ang kahulugan nito. Kung ang DLL ay dynamic na na-load, ito ay magiging NULL. Kapag naglo-load nang statically, magiging non-zero ang pointer na ito.

Kung matagumpay, ang function DllMain dapat ibalik ang TOTOO. Kung may nangyaring error, ibabalik ang FALSE at wawakasan ang karagdagang pagkilos.

Magkomento. Kung hindi mo isusulat ang iyong sariling DllMain() function, gagamitin ng compiler ang karaniwang bersyon, na nagbabalik lamang ng TRUE.

Pag-export ng mga function mula sa mga DLL

Para sa isang application na ma-access ang mga dynamic na function ng library, ang bawat function ay dapat maghawak ng isang row sa talahanayan ng mga na-export na DLL function. Mayroong dalawang paraan upang magdagdag ng function sa talahanayang ito sa oras ng pag-compile.

__declspec(dllexport) na pamamaraan

Maaari kang mag-export ng function mula sa isang DLL sa pamamagitan ng paglalagay ng prefix sa paglalarawan nito gamit ang __declspec (dllexport) modifier. Bilang karagdagan, ang MFC ay may kasamang ilang macro na tumutukoy sa __declspec (dllexport), kabilang ang AFX_CLASS_EXPORT, AFX_DATA_EXPORT, at AFX_API_EXPORT.

Ang pamamaraang __declspec ay hindi ginagamit nang kasingdalas ng pangalawang paraan, na gumagana sa mga file ng kahulugan ng module (.def), at nagbibigay-daan para sa mas mahusay na kontrol sa proseso ng pag-export.

Mga File ng Depinisyon ng Module

Ang syntax para sa mga .def file sa Visual C++ ay medyo diretso, pangunahin dahil ang mga kumplikadong opsyon na ginamit sa mga naunang bersyon ng Windows ay hindi na nalalapat sa Win32. Tulad ng magiging malinaw mula sa sumusunod na simpleng halimbawa, ang .def file ay naglalaman ng pangalan at paglalarawan ng library, pati na rin ang isang listahan ng mga na-export na function:

MyDLL.def LIBRARY "MyDLL" DESCRIPTION "MyDLL - halimbawa DLL library" EXPORTS MyFunction @1

Sa linya ng pag-export ng function, maaari mong ipahiwatig ang serial number nito sa pamamagitan ng paglalagay ng simbolo na @ sa harap nito. Ang numerong ito ay gagamitin kapag nakikipag-ugnayan GetProcAddress(). Sa katunayan, ang compiler ay nagtatalaga ng mga sequence number sa lahat ng na-export na mga bagay. Gayunpaman, ang paraan ng paggawa nito ay medyo hindi mahuhulaan maliban kung tahasan mong italaga ang mga numerong ito.

Maaari mong gamitin ang NONAME parameter sa export string. Pinipigilan nito ang compiler na isama ang pangalan ng function sa DLL export table:

MyFunction @1 NONAME

Minsan nakakatipid ito ng maraming espasyo sa DLL file. Hindi "mapapansin" ng mga application na gumagamit ng import library para tahasang mag-link ng mga DLL dahil ang implicit na pag-link ay gumagamit ng mga sequence number nang awtomatiko. Ang mga application na dynamic na naglo-load ng mga DLL ay kailangang pumasa GetProcAddress sequence number, hindi ang function name.

Kapag ginagamit ang nasa itaas, ang def file na naglalarawan sa na-export na mga function ng DLL ay maaaring hindi, halimbawa, tulad nito:

#define EXPORT extern "C" __declspec (dllexport) EXPORT int CALLBACK MyFunction(char *str); tulad nito: extern "C" int CALLBACK MyFunction(char *str);

Pag-export ng mga klase

Ang paglikha ng isang .def file upang i-export kahit na ang mga simpleng klase mula sa isang dynamic na library ay maaaring maging mahirap. Kakailanganin mong tahasang i-export ang bawat function na maaaring magamit ng isang panlabas na application.

Kung titingnan mo ang file ng paglalaan ng memorya na ipinatupad sa klase, mapapansin mo ang ilang mga hindi pangkaraniwang tampok. Lumalabas na mayroong mga implicit na constructor at destructors, mga function na idineklara sa MFC macros, sa partikular na _DECLARE_MESSAGE_MAP, pati na rin ang mga function na isinulat ng programmer.

Bagama't posibleng i-export ang bawat isa sa mga function na ito nang paisa-isa, mayroong isang mas madaling paraan. Kung gagamitin mo ang AFX_CLASS_EXPORT macromodifier sa isang deklarasyon ng klase, ang compiler mismo ang bahala sa pag-export ng mga kinakailangang function na nagpapahintulot sa application na gamitin ang klase na nasa DLL.

Memory DLL

Hindi tulad ng mga static na aklatan, na mahalagang bahagi ng application code, ang mga dynamic na link na aklatan sa 16-bit na bersyon ng Windows ay humawak ng memorya nang medyo naiiba. Sa ilalim ng Win 16, ang DLL memory ay matatagpuan sa labas ng task address space. Ang paglalagay ng mga dynamic na aklatan sa pandaigdigang memorya ay naging posible na ibahagi ang mga ito sa iba't ibang gawain.

Sa Win32, ang isang DLL ay naninirahan sa lugar ng memorya ng proseso na naglo-load nito. Ang bawat proseso ay binibigyan ng hiwalay na kopya ng "global" na memorya ng DLL, na muling ini-initialize tuwing may bagong proseso na naglo-load nito. Nangangahulugan ito na ang dynamic na library ay hindi maibabahagi sa shared memory, tulad ng sa Winl6.

Gayunpaman, sa pamamagitan ng pagsasagawa ng isang serye ng masalimuot na pagmamanipula sa isang segment ng data ng DLL, posible na lumikha ng isang shared memory area para sa lahat ng mga proseso na gumagamit ng DLL.

Sabihin nating mayroon tayong hanay ng mga integer na dapat gamitin ng lahat ng prosesong naglo-load ng isang ibinigay na DLL. Maaari itong i-program tulad ng sumusunod:

#pragma data_seg(".myseg") int sharedlnts ; // other public variables #pragma data_seg() #pragma comment(lib, "msvcrt" "-SECTION:.myseg,rws");

Ang lahat ng mga variable na idineklara sa pagitan ng #pragma data_seg() na mga direktiba ay inilalagay sa.myseg segment. Ang direktiba ng #pragma comment() ay hindi isang regular na komento. Inutusan nito ang C runtime library na markahan ang bagong partition bilang read, write, at shareable.

Buong DLL compilation

Kung ang dynamic na proyekto ng library ay ginawa gamit ang AppWizard at ang .def file ay binago nang naaayon, ito ay sapat na. Kung manu-mano kang lumikha ng mga file ng proyekto o sa ibang paraan nang walang tulong ng AppWizard, dapat mong isama ang parameter na /DLL sa command line ng editor ng link. Ito ay lilikha ng isang DLL sa halip na isang standalone executable.

Kung mayroong LIBRART na linya sa .def file, hindi mo kailangang tahasang tukuyin ang parameter na /DLL sa command line ng editor ng link.

Ang MFC ay may ilang espesyal na pag-uugali patungkol sa paggamit ng dynamic na library ng mga MFC library. Ang susunod na seksyon ay nakatuon sa isyung ito.

DLL at MFC

Ang programmer ay hindi kinakailangang gumamit ng MFC kapag gumagawa ng mga dynamic na aklatan. Gayunpaman, ang paggamit ng MFC ay nagbubukas ng maraming napakahalagang posibilidad.

Mayroong dalawang antas ng paggamit ng istraktura ng MFC sa isang DLL. Ang una ay isang regular na dynamic na library na nakabatay sa MFC, MFC DLL(regular na MFC DLL). Maaari itong gumamit ng MFC, ngunit hindi maaaring magpasa ng mga pointer sa mga bagay na MFC sa pagitan ng mga DLL at application. Ang ikalawang antas ay ipinatupad sa mga dynamic na MFC extension(Mga extension ng MFC DLL). Ang paggamit ng ganitong uri ng dynamic na library ay nangangailangan ng ilang karagdagang pagsisikap sa pag-setup, ngunit nagbibigay-daan sa mga pointer sa mga bagay na MFC na malayang makipagpalitan sa pagitan ng DLL at ng application.

Mga regular na MFC DLL

Nagbibigay-daan sa iyo ang mga regular na MFC DLL na gumamit ng MFC sa mga dynamic na library. Gayunpaman, ang mga application na nag-a-access sa mga naturang aklatan ay hindi kinakailangang itayo sa MFC. Maaaring gamitin ng mga regular na DLL ang MFC sa anumang paraan, kabilang ang paggawa ng mga bagong klase sa DLL batay sa mga klase ng MFC at pag-export ng mga ito sa mga application.

Gayunpaman, ang mga regular na DLL ay hindi maaaring makipagpalitan ng mga pointer sa mga klase na nagmula sa MFC na may mga application.

Kung kailangan ng iyong application na makipagpalitan ng mga pointer sa mga bagay ng mga klase ng MFC o mga derivatives ng mga ito gamit ang isang DLL, dapat nitong gamitin ang extension ng DLL na inilalarawan sa susunod na seksyon.

Ang arkitektura ng mga regular na DLL ay idinisenyo upang magamit ng iba pang mga kapaligiran sa programming, tulad ng Visual Basic at PowerBuilder.

Kapag gumagawa ng isang regular na MFC DLL gamit ang AppWizard, isang bagong proyekto ng uri MFC AppWizard (dll). Sa unang dialog box ng Application Wizard, dapat kang pumili ng isa sa mga mode para sa mga regular na dynamic na aklatan: "Regular DLL na may MFC statistically linked" o "Regular DLL using shared MFC DLL". Ang una ay nagbibigay ng static, at ang pangalawa - dynamic na koneksyon ng mga library ng MFC. Maaari mong baguhin ang MFC sa DLL connection mode sa ibang pagkakataon gamit ang combo box sa General tab ng Project settings dialog.

Pamamahala ng MFC State Information

Ang bawat module ng proseso ng MFC ay naglalaman ng impormasyon tungkol sa estado nito. Kaya, ang impormasyon ng estado ng isang DLL ay iba sa impormasyon ng estado ng application na tumawag dito. Samakatuwid, ang anumang mga function na na-export mula sa library na direktang na-access mula sa mga application ay dapat sabihin sa MFC kung anong impormasyon ng estado ang gagamitin. Sa isang regular na MFC DLL na gumagamit ng mga dynamic na library ng MFC, bago tumawag sa anumang MFC routine, dapat mong ilagay ang sumusunod na linya sa simula ng na-export na function:

AFX_MANAGE_STATE(AfxGetStaticModuleState()) ;

Tinutukoy ng pahayag na ito ang paggamit ng naaangkop na impormasyon ng estado sa panahon ng pagpapatupad ng function na tumatawag sa subroutine na ito.

Mga Dynamic na MFC Extension

Pinapayagan ka ng MFC na lumikha ng mga DLL na nakikita ng mga application hindi bilang isang hanay ng mga hiwalay na function, ngunit bilang mga extension ng MFC. Sa ganitong uri ng DLL, maaari kang lumikha ng mga bagong klase na nagmula sa mga klase ng MFC at gamitin ang mga ito sa iyong mga application.

Upang payagan ang mga pointer sa mga bagay na MFC na malayang makipagpalitan sa pagitan ng isang application at isang DLL, kailangan mong lumikha ng isang dynamic na extension ng MFC. Ang mga DLL ng ganitong uri ay kumokonekta sa mga dynamic na library ng MFC sa parehong paraan tulad ng anumang application na gumagamit ng dynamic na extension ng MFC.

Upang lumikha ng bagong dynamic na extension ng MFC, ang pinakamadaling paraan ay ang paggamit ng Application Wizard upang italaga ang uri ng proyekto MFC AppWizard (dll) at sa hakbang 1 paganahin ang "MFC Extension DLL" mode. Bilang resulta, itatalaga sa bagong proyekto ang lahat ng kinakailangang katangian ng dynamic na extension ng MFC. Bilang karagdagan, ang isang function ay malilikha DllMain para sa isang DLL, na nagsasagawa ng ilang partikular na operasyon upang masimulan ang extension ng DLL. Pakitandaan na ang mga dynamic na aklatan ng ganitong uri ay hindi at hindi dapat maglaman ng mga bagay na nagmula sa CWinApp.

Pagsisimula ng Mga Dynamic na Extension

Upang magkasya sa MFC framework, ang mga dynamic na extension ng MFC ay nangangailangan ng karagdagang paunang pag-setup. Ang mga kaukulang operasyon ay ginagawa ng function DllMain. Tingnan natin ang isang halimbawa ng function na ito na nilikha ng AppWizard wizard.

Static AFX_EXTENSION_MODULE MyExtDLL = ( NULL, NULL ); extern "C" int APIENTRY DllMain(HINSTANCE hinstance, DWORD dwReason, LPVOID IpReserved) ( kung (dwReason == DLL_PROCESS_ATTACH) ( TRACED("MYEXT.DLL Initializing!\n") ; // Extension DLL one-time initialization AfxDLLInitExtension(Extension ng DLL na isang beses na AfxDLLInitExtension. , hinstance) ; // Ipasok ang DLL na ito sa resource chain new CDynLinkLibrary(MyExtDLL); ) else if (dwReason == DLL_PROCESS_DETACH) ( TRACED("MYEXT.DLL Terminating!\n") ; ) return 1; // ok )

Ang pinakamahalagang bahagi ng function na ito ay ang tawag AfxInitExtensionModule. Ito ang pagsisimula ng isang dynamic na library, na nagbibigay-daan dito upang gumana nang tama bilang bahagi ng istraktura ng MFC. Ang mga argumento ng function na ito ay ang DLL library descriptor na ipinasa sa DllMain at ang AFX_EXTENSION_MODULE structure, na naglalaman ng impormasyon tungkol sa dynamic na library na konektado sa MFC.

Hindi na kailangang i-initialize ang AFX_EXTENSION_MODULE structure nang tahasan. Gayunpaman, dapat itong ipahayag. Hahawakan ng constructor ang initialization CDynLinkLibrary. Kailangan mong lumikha ng isang klase sa DLL CDynLinkLibrary. Ang constructor nito ay hindi lamang magpapasimula sa AFX_EXTENSION_MODULE structure, ngunit magdaragdag din ng bagong library sa listahan ng mga DLL na maaaring gamitin ng MFC.

Naglo-load ng mga dynamic na MFC extension

Simula sa bersyon 4.0, pinapayagan ka ng MFC na dynamic na mag-load at mag-unload ng mga DLL, kabilang ang mga extension. Upang maisagawa nang tama ang mga operasyong ito sa nilikhang DLL sa pagpapaandar nito DllMain sa sandali ng pagdiskonekta mula sa proseso, kailangan mong magdagdag ng isang tawag AfxTermExtensionModule. Ang huling function ay ipinasa ang AFX_EXTENSION_MODULE structure na ginamit na sa itaas bilang isang parameter. Upang gawin ito sa teksto DllMain kailangan mong idagdag ang mga sumusunod na linya.

If(dwReason == DLL_PROCESS_DETACH) ( AfxTermExtensionModule(MyExtDLL); )

Gayundin, tandaan na ang bagong DLL ay isang dynamic na extension at dapat na i-load at i-unload nang pabago-bago gamit ang mga function. AfxLoadLibrary At AfxFreeLibrary, ngunit hindi LoadLibrary At LibrengLibrary.

Pag-export ng mga function mula sa mga dynamic na extension

Tingnan natin ngayon kung paano na-export ang mga function at klase mula sa isang dynamic na extension sa isang application. Bagama't maaari mong manu-manong idagdag ang lahat ng pinalawak na pangalan sa DEF file, mas mainam na gumamit ng mga modifier para sa mga deklarasyon ng mga na-export na klase at function, gaya ng AFX_EXT_CLASS at AFX_EXT_API, halimbawa:

Class AFX_EXT_CLASS CMyClass: pampublikong CObject (// Ang deklarasyon ng iyong klase ) ay walang bisa AFX_EXT_API MyFunc() ;

Mayroong tatlong mga paraan upang mag-load ng isang DLL:

a) implicit;

c) ipinagpaliban.

Tingnan natin ang implicit DLL loading. Upang bumuo ng isang application na idinisenyo para sa implicit na pag-load ng DLL, dapat ay mayroon kang:

Kasama sa library ang file na may mga paglalarawan ng mga ginamit na bagay mula sa DLL (mga prototype ng function, klase at mga deklarasyon ng uri). Ang file na ito ay ginagamit ng compiler.

LIB file na may listahan ng mga na-import na identifier. Dapat idagdag ang file na ito sa mga setting ng proyekto (sa listahan ng mga library na ginamit ng linker).

Ang proyekto ay pinagsama-sama sa karaniwang paraan. Gamit ang object modules at LIB file, pati na rin ang pagsasaalang-alang ng mga link sa mga imported na identifier, ang linker (linker, link editor) ay bumubuo ng boot EXE module. Sa module na ito, naglalagay din ang linker ng seksyon ng pag-import na naglilista ng mga pangalan ng lahat ng kinakailangang DLL modules. Para sa bawat DLL, ang seksyon ng pag-import ay tumutukoy kung aling mga function at variable na pangalan ang tinutukoy sa code ng executable file. Ang impormasyong ito ay gagamitin ng operating system loader.

Ano ang mangyayari sa yugto ng pagpapatupad ng aplikasyon ng kliyente? Pagkatapos ilunsad ang EXE module, ginagawa ng operating system loader ang mga sumusunod na operasyon:

1. Lumilikha ng isang virtual na puwang ng address para sa isang bagong proseso at nagpapalabas ng isang maipapatupad na module dito;

2. Pinag-aaralan ang seksyon ng pag-import, tinutukoy ang lahat ng kinakailangang DLL modules at ipinapalabas din ang mga ito sa puwang ng address ng proseso.

Ang isang DLL ay maaaring mag-import ng mga function at variable mula sa isa pang DLL. Nangangahulugan ito na maaaring mayroon itong sariling seksyon ng pag-import, kung saan kailangan mong ulitin ang parehong mga hakbang. Bilang isang resulta, maaaring tumagal ng medyo mahabang oras para masimulan ang proseso.

Kapag ang EXE module at lahat ng DLL modules ay nai-mapa sa address space ng proseso, ang pangunahing thread nito ay handa na para sa execution, at ang application ay magsisimulang tumakbo.

Ang mga disadvantages ng implicit loading ay ang ipinag-uutos na pag-load ng library, kahit na ang mga function nito ay hindi tinatawag, at, nang naaayon, ang ipinag-uutos na kinakailangan para sa pagkakaroon ng library kapag nagli-link.

Ang tahasang paglo-load ng isang DLL ay nag-aalis ng mga kawalan na nabanggit sa itaas sa gastos ng ilang pagiging kumplikado ng code. Ang programmer mismo ay kailangang alagaan ang paglo-load ng DLL at pagkonekta sa mga na-export na function. Ngunit ang tahasang paglo-load ay nagbibigay-daan sa iyo na mag-load ng mga DLL kung kinakailangan at nagbibigay-daan sa programa na pangasiwaan ang mga sitwasyon na lumitaw sa kawalan ng isang DLL.

Sa kaso ng tahasang pag-load, ang proseso ng pagtatrabaho sa DLL ay nangyayari sa tatlong yugto:

1. I-load ang DLL gamit ang function LoadLibrary(o ang pinahabang analogue nito LoadLibraryEx). Kung matagumpay ang pag-load, ibabalik ng function ang isang hLib descriptor ng uri ng HMODULE, na nagbibigay-daan sa karagdagang access sa DLL na ito.

2. Mga function na tawag GetProcAddress upang makakuha ng mga pointer sa mga kinakailangang function o iba pang mga bagay. Bilang unang parameter ang function GetProcAddress tumatanggap ng hLib descriptor, at bilang pangalawang parameter ang address ng string na may pangalan ng na-import na function. Ang resultang pointer ay gagamitin ng kliyente. Halimbawa, kung ito ay isang function pointer, kung gayon ang nais na function ay tinatawag.

3. Kapag hindi na kailangan ang naka-load na dynamic na library, inirerekumenda na palayain ito sa pamamagitan ng pagtawag sa function LibrengLibrary. Ang pagpapalaya sa isang library ay hindi nangangahulugan na ang operating system ay agad na aalisin ito mula sa memorya. Ang pagkaantala sa pag-unload ay ibinibigay para sa kaso kapag ang parehong DLL ay kinakailangan muli sa pamamagitan ng ilang proseso pagkatapos ng ilang oras. Ngunit kung may mga problema sa RAM, inalis muna ng Windows ang mga libreng library mula sa memorya.

Tingnan natin ang tamad na pag-load ng isang DLL. Ang delay-load na DLL ay isang implicitly linked DLL na hindi nalo-load hanggang ma-access ng code ang ilang identifier na na-export mula dito. Ang ganitong mga DLL ay maaaring maging kapaki-pakinabang sa mga sumusunod na sitwasyon:

Kung ang isang application ay gumagamit ng maraming DLL, ang pagsisimula ay maaaring tumagal ng mahabang panahon para sa loader na imapa ang lahat ng mga DLL sa address space ng proseso. Ang mga tamad na naglo-load ng DLL ay malulutas ang problemang ito sa pamamagitan ng pamamahagi ng paglo-load ng DLL sa buong pagpapatupad ng application.

Kung ang isang application ay idinisenyo upang gumana sa iba't ibang mga bersyon ng OS, ang ilan sa mga function ay maaaring lumitaw lamang sa mga susunod na bersyon ng OS at hindi magagamit sa kasalukuyang bersyon. Ngunit kung ang programa ay hindi tumawag sa isang tiyak na pag-andar, kung gayon hindi nito kailangan ang DLL, at maaari itong ligtas na magpatuloy sa trabaho. Kapag tinutukoy ang isang hindi umiiral

Maaaring i-configure ang function upang magbigay ng naaangkop na babala sa user.

Upang ipatupad ang tamad na paraan ng paglo-load, kailangan mo ring idagdag sa listahan ng mga linker library hindi lamang ang kinakailangang import library na MyLib.lib, kundi pati na rin ang system import library delayimp.lib. Bilang karagdagan, kailangan mong idagdag ang flag na / delayload:MyLib.dll sa mga opsyon sa linker.

Ang mga setting na ito ay nagiging sanhi ng linker upang maisagawa ang mga sumusunod na operasyon:

Magpatupad ng isang espesyal na function sa EXE module
delayLoadHelper;

Alisin ang MyLib.dll mula sa executable module import na seksyon upang ang operating system loader ay hindi magtangka na implicitly load ang library na ito sa panahon ng application boot phase;

Magdagdag ng bagong pinagpaliban na seksyon ng pag-import sa EXE file na may listahan ng mga function na na-import mula sa MyLib.dll;

I-convert ang mga function na tawag mula sa DLL sa mga tawag
delayLoadhelper.

Sa yugto ng pagpapatupad ng application, ang isang function na tawag mula sa isang DLL ay ipinatupad sa pamamagitan ng pagtawag sa delayLoadHelper. Ang function na ito, gamit ang impormasyon mula sa pinagpaliban na seksyon ng pag-import, ay unang tumatawag sa LoadLibrary at pagkatapos ay GetprocAddress. Matapos matanggap ang address ng isang function ng DLL, tinitiyak ng delayLoadHelper na sa hinaharap ang function na ito ng DLL ay direktang tinatawag. Tandaan na ang bawat function sa DLL ay isa-isang na-configure sa unang pagkakataong ito ay tinawag.

Mayroong dalawang paraan upang mag-load ng isang DLL: tahasang pag-link at implicit na pag-link.

Sa implicit linking, ang mga function ng load DLL ay idinaragdag sa import section ng calling file. Kapag inilunsad ang naturang file, sinusuri ng operating system loader ang seksyon ng pag-import at kasama ang lahat ng tinukoy na library. Dahil sa pagiging simple nito, ang pamamaraang ito ay napakapopular; ngunit ang pagiging simple ay pagiging simple, at ang implicit na layout ay may ilang mga disadvantage at limitasyon:

ang lahat ng konektadong DLL ay palaging na-load, kahit na ang programa ay hindi kailanman na-access ang alinman sa mga ito sa buong operating session;

kung ang hindi bababa sa isa sa mga kinakailangang DLL ay nawawala (o ang DLL ay hindi nag-e-export ng hindi bababa sa isang kinakailangang function) - ang paglo-load ng executable file ay naaantala ng mensaheng "Hindi matagpuan ang library ng dynamic na link" (o isang katulad nito) - kahit na ang kawalan ng DLL na ito ay hindi kritikal upang maisagawa ang programa. Halimbawa, ang isang text editor ay maaaring gumana nang maayos sa isang minimal na pagsasaayos - nang walang module sa pag-print, nagpapakita ng mga talahanayan, mga graph, mga formula at iba pang mga menor de edad na bahagi, ngunit kung ang mga DLL na ito ay na-load sa pamamagitan ng implicit na pag-link - gusto mo o hindi, kakailanganin mong "hilahin" sila kasama mo.

ang DLL ay hinanap sa sumusunod na pagkakasunud-sunod: sa direktoryo na naglalaman ng file ng pagtawag; sa kasalukuyang direktoryo ng proseso; sa direktoryo ng system %Windows%System%; sa pangunahing direktoryo %Windows%; sa mga direktoryo na tinukoy sa PATH variable. Imposibleng magtakda ng ibang landas sa paghahanap (o sa halip, posible, ngunit mangangailangan ito ng paggawa ng mga pagbabago sa pagpapatala ng system, at ang mga pagbabagong ito ay makakaapekto sa lahat ng mga prosesong tumatakbo sa system - na hindi maganda).

Ang tahasang pag-link ay nag-aalis ng lahat ng mga kawalan na ito - sa halaga ng ilang pagiging kumplikado ng code. Ang programmer mismo ay kailangang mag-ingat sa pag-load ng DLL at pagkonekta sa mga na-export na pag-andar (habang hindi nalilimutan ang tungkol sa kontrol ng error, kung hindi man sa isang punto ay mag-freeze ang system). Ngunit binibigyang-daan ka ng tahasang pag-link na i-load ang mga DLL kung kinakailangan at binibigyan ang programmer ng pagkakataong mag-isa na pangasiwaan ang mga sitwasyon kung saan nawawala ang DLL. Maaari kang pumunta nang higit pa - huwag tahasang itakda ang pangalan ng DLL sa programa, ngunit i-scan ang ganoon at ganoong direktoryo para sa pagkakaroon ng mga dynamic na aklatan at ikonekta ang lahat ng natagpuan sa application. Ito ay eksakto kung paano gumagana ang mekanismo ng suporta ng plug-in sa sikat na FAR file manager (at hindi lamang dito).

Paggamit DLL(dynamic link library) ay malawakang ginagamit sa Windows programming. DLL ay talagang bahagi ng code ng isang executable file na may extension DLL. Anumang programa ay maaaring tumawag DLL.

Advantage DLL ay ang mga sumusunod:

  • Muling paggamit ng code.
  • Ibahagi ang code sa pagitan ng mga application.
  • Pagbabahagi ng code.
  • Pinahusay na pagkonsumo ng mapagkukunan sa Windows.

Paglikha ng isang DLL

Nasa listahan file piliin ang Bago -> Iba pa. Sa dialog box sa tab Bago pumili DLL Wizard. Awtomatikong gagawin ang isang module - isang walang laman na template para sa hinaharap DLL.

Syntax ng DLL

Upang makabuo DLL, piliin Proyekto -> Bumuo Pangalan ng proyekto .

Visibility ng mga function at procedure

Ang mga function at pamamaraan ay maaaring lokal at i-export mula sa DLL.

Lokal

Ang mga lokal na pag-andar at pamamaraan ay maaaring gamitin sa loob DLL. Sa loob lamang ng silid-aklatan sila makikita at walang programa ang makakatawag sa kanila mula sa labas.

Na-export

Maaaring gamitin sa labas ang mga na-export na function at procedure DLL. Maaaring tawagan ng ibang mga programa ang mga naturang function at procedure.

Ginagamit ng source code sa itaas ang na-export na function. Ang pangalan ng function ay sumusunod sa isang nakalaan na salita Mga pag-export.

Naglo-load ng mga DLL

Ang Delphi ay may dalawang uri ng paglo-load DLL:

Static loading

Kapag sinimulan mo ang application, awtomatiko itong naglo-load. Ito ay nananatili sa memorya sa buong pagpapatupad ng programa. Napakadaling gamitin. Magdagdag lamang ng isang salita panlabas pagkatapos magdeklara ng isang function o procedure.

Function SummaTotal(factor: integer): integer; magparehistro; panlabas na "Example.dll";

Kung DLL ay hindi mahahanap, ang programa ay patuloy na tatakbo.

na-load sa memorya kung kinakailangan. Ang pagpapatupad nito ay mas kumplikado dahil kailangan mong i-load at i-unload ito mula sa memorya mismo. Ang memorya ay ginagamit nang mas matipid, kaya ang application ay tumatakbo nang mas mabilis. Dapat tiyakin ng programmer mismo na gumagana nang tama ang lahat. Upang gawin ito kailangan mo:

  • Ipahayag ang uri ng function o pamamaraan na inilalarawan.
  • I-load ang library sa memorya.
  • Kunin ang address ng isang function o procedure sa memorya.
  • Tumawag ng isang function o procedure.
  • I-unload ang library mula sa memorya.

Deklarasyon ng isang uri na naglalarawan ng isang function

uri TSumaTotal = function(factor: integer): integer;

Nilo-load ang library

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

Kumuha kami ng pointer sa function

@SummaTotal:= GetProcAddress(dll_instance, "SummaTotal");

Pagtawag ng isang function

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

Pag-alis ng library mula sa memorya

FreeLibrary(dll_instance);

Dynamic na tawag DLL nangangailangan ng mas maraming trabaho, ngunit mas madaling pamahalaan ang mga mapagkukunan sa memorya. Kung kailangan mong gamitin DLL sa loob ng programa, kung gayon ang static na paglo-load ay mas mainam. Huwag kalimutang gumamit ng block subukan...maliban At subukan...sa wakas upang maiwasan ang mga error sa panahon ng pagpapatupad ng programa.

I-export ang mga string

Nilikha DLL gamit ang Delphi, maaari ding gamitin sa mga programang nakasulat sa iba pang mga programming language. Para sa kadahilanang ito, hindi kami maaaring gumamit ng anumang uri ng data. Ang mga uri na umiiral sa Delphi ay maaaring wala sa ibang mga wika. Maipapayo na gumamit ng mga native na uri ng data mula sa Linux o Windows. Ang aming DLL maaaring gamitin ng ibang programmer na gumagamit ng ibang programming language.

Maaaring gamitin mga linya At mga dynamic na array V DLL, nakasulat sa Delphi, ngunit para dito kailangan mong ikonekta ang module ShareMem sa seksyon gamit V DLL at ang program na gagamit nito. Bilang karagdagan, ang ad na ito ay dapat na una sa seksyon gamit bawat file ng proyekto.

Mga uri string, tulad ng alam natin, C, C++ at iba pang mga wika ay hindi umiiral, kaya ipinapayong gamitin sa halip PChar.

Halimbawa ng DLL

library Example_dll; gumagamit ng SysUtils, Mga Klase; var (Pagdedeklara ng mga variable) k1: integer; k2:integer; (Ipahayag ang isang function) function SummaTotal(factor: integer): integer; magparehistro; simula factor:= factor * k1 + factor; factor:= factor * k2 + factor; resulta:= factor; wakas; (Ine-export ang function para sa karagdagang paggamit ng programa) exports SummaTotal; (Initializing variables) magsisimula k1:= 2; k2:= 5; wakas.

Halimbawa ng pagtawag sa isang function mula sa isang DLL

yunit Yunit1; interface ay gumagamit ng Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); pribado ( Private declarations ) public ( Public declarations ) end; type TSummaTotal = function(factor: integer): integer; var Form1: TForm1; pagpapatupad ($R *.dfm) na pamamaraan TForm1.Button1Click(Sender: TObject); var dll_instance: Thandle; SummaTotal: TSummaTotal; simulan ang dll_instance:= LoadLibrary("Example_dll.dll"); @SummaTotal:= GetProcAddress(dll_instance, "SummaTotal"); Label1.Caption:= IntToStr(SummaTotal(5)); FreeLibrary(dll_instance); wakas; wakas.

Tulad ng malamang na alam mo, ang mga dynamic na link libraries (DLLs) ay gumagamit ng C language convention kapag nagdedeklara ng mga na-export na bagay, habang ang C++ ay gumagamit ng isang bahagyang naiibang sistema para sa pagbuo ng mga pangalan kapag pinagsama-sama, kaya hindi mo na lang i-export ang mga function - mga pamamaraan ng isang C++ na klase at pagkatapos ay gamitin ang mga ito sa code ng application ng kliyente (pagkatapos nito, ang ibig sabihin ng kliyente ay isang application na gumagamit ng DLL). Gayunpaman, maaari itong gawin gamit ang mga interface na magagamit sa DLL at sa application ng kliyente. Ang pamamaraang ito ay napakalakas at sa parehong oras ay matikas, dahil... nakikita lamang ng kliyente ang abstract na interface, at ang aktwal na klase na nagpapatupad ng lahat ng mga function ay maaaring maging anuman. Ang teknolohiya ng COM (Component Object Model) ng Microsoft ay binuo sa isang katulad na ideya (kasama ang karagdagang pag-andar, siyempre). Ipapakita sa iyo ng artikulong ito kung paano gamitin ang diskarte na "klase" gamit ang isang interface na tulad ng COM nang maaga (oras ng pag-compile) at huli (habang tumatakbo ang programa) na nagbubuklod.

Kung nakatrabaho mo na ang mga DLL, alam mo na na ang mga DLL ay may espesyal na function na DllMain(). Ang function na ito ay katulad ng WinMain, o main(), dahil ito ay uri ng entry point sa isang DLL. Awtomatikong tinatawag ng operating system ang function na ito kapag na-load at na-unload ang DLL. Karaniwan ang function na ito ay hindi ginagamit para sa anumang bagay.

Mayroong dalawang paraan para sa pagkonekta ng isang DLL sa isang proyekto - maaga (sa pagsasama-sama ng programa) at huli (sa panahon ng pagpapatupad ng programa) pag-link. Ang mga pamamaraan ay naiiba sa paraan ng pag-load ng DLL at kung paano tinawag ang mga function na ipinatupad at na-export mula sa DLL.

Maagang pagbubuklod (sa panahon ng pagsasama-sama ng programa)

Sa paraan ng pag-link na ito, awtomatikong nilo-load ng operating system ang DLL kapag nagsimula ang program. Gayunpaman, kinakailangan na ang proyektong binuo ay may kasamang .lib file (library file) na naaayon sa DLL na ito. Tinutukoy ng file na ito ang lahat ng na-export na DLL object. Ang mga deklarasyon ay maaaring maglaman ng mga regular na C function o klase. Ang kailangan lang gawin ng kliyente ay gamitin ang .lib file na ito at isama ang DLL header file - at awtomatikong ilo-load ng OS ang DLL na ito. Tulad ng nakikita mo, ang pamamaraang ito ay mukhang napakadaling gamitin, dahil... lahat ay transparent. Gayunpaman, dapat ay napansin mo na ang client code ay kailangang muling i-compile sa tuwing babaguhin ang DLL code at, nang naaayon, isang bagong .lib file ang nabuo. Kung ito ay maginhawa para sa iyong aplikasyon ay nasa iyo ang pagpapasya. Maaaring ideklara ng isang DLL ang mga function na nais nitong i-export sa dalawang pamamaraan. Ang karaniwang paraan ay ang paggamit ng mga .def file. Ang .def file na ito ay isang listahan lamang ng mga function na na-export mula sa DLL.

//================================================================ =========================== // .def file LIBRARY myfirstdll.dll DESCRIPTION "My first DLL" EXPORTS MyFunction //=== ================= ================================== ==================== // DLL header na isasama sa client code bool MyFunction(int parms); //================================================================ =========================== // pagpapatupad ng function sa DLL bool MyFunction(int parms) ( // gawin ang lahat ng kailangan ............ )

Sa tingin ko, hindi sinasabi na sa halimbawang ito ay isang function lamang, MyFunction, ang na-export. Ang pangalawang paraan ng pagdedeklara ng mga na-export na bagay ay tiyak, ngunit mas malakas: maaari mong i-export hindi lamang ang mga function, kundi pati na rin ang mga klase at variable. Tingnan natin ang snippet ng code na nabuo noong ginawa ng VisualC++ AppWizard ang DLL. Ang mga komentong kasama sa listahan ay sapat na upang maunawaan kung paano gumagana ang lahat.

//================================================================ =========================== // DLL header na dapat isama sa client code /* Ang sumusunod na ifdef block ay isang standard na paraan para sa paglikha ng isang macro na nagpapadali sa pag-export mula sa isang DLL. Ang lahat ng mga file sa DLL na ito ay pinagsama-sama sa isang partikular na key na MYFIRSTDLL_EXPORTS. Ang key na ito ay hindi tinukoy para sa alinman sa mga proyekto na gumagamit ng DLL na ito. Kaya, nakikita ng anumang proyekto na kinabibilangan ng file na ito ang mga function ng MYFIRSTDLL_API bilang na-import mula sa DLL, habang ang DLL mismo ay nakikita ang parehong mga function na na-export. */ #ifdef MYFIRSTDLL_EXPORTS #define MYFIRSTDLL_API __declspec(dllexport) #else #define MYFIRSTDLL_API __declspec(dllimport) #endif // Ang klase ay na-export mula sa test2.dll class na MYFIRSTDLL_API (///id CMyFirstDLL_API mo dito: publicFirstDDo: maaaring magdagdag ng iyong sariling mga pamamaraan. ); extern MYFIRSTDLL_API int nMyFirstDll; MYFIRSTDLL_API int fnMyFunction(walang bisa);

Sa panahon ng DLL compilation, ang MYFIRSTDLL_EXPORTS key ay tinukoy, kaya ang __declspec(dllexport) na keyword ay naka-prefix sa mga na-export na deklarasyon ng object. At kapag ang client code ay pinagsama-sama, ang susi na ito ay hindi natukoy at ang mga bagay ay may prefix na __declspec(dllimport) upang malaman ng kliyente kung aling mga bagay ang ini-import mula sa DLL.

Sa parehong mga kaso, ang kailangan lang gawin ng kliyente ay idagdag ang myfirstdll.lib file sa proyekto at isama ang isang header file na nagdedeklara ng mga bagay na ini-import mula sa DLL, at pagkatapos ay gamitin ang mga bagay na iyon (mga function, klase at variable) nang eksakto kung paano. sila ay tinukoy at ipinatupad nang lokal sa proyekto. Ngayon tingnan natin ang isa pang paraan ng paggamit ng mga DLL, na kadalasang mas maginhawa at makapangyarihan.

Late binding (habang tumatakbo ang program)

Kapag ginamit ang late binding, ang DLL ay hindi awtomatikong na-load kapag nagsimula ang programa, ngunit direkta sa code, kung saan ito kinakailangan. Walang mga .lib na file ang kailangang gamitin, kaya ang client application ay hindi nangangailangan ng recompilation kapag nagbago ang DLL. Ang pagli-link na ito ay napakalakas dahil IKAW ang magpapasya kung kailan at aling DLL ang ilo-load. Halimbawa, sabihin nating nagsusulat ka ng isang laro na gumagamit ng DirectX at OpenGL. Maaari mo lamang isama ang lahat ng kinakailangang code sa maipapatupad na file, ngunit pagkatapos ay magiging imposible lamang na maunawaan ang anuman. O maaari mong ilagay ang DirectX code sa isang DLL at ang OpenGL code sa isa pa at statically ikonekta ang mga ito sa proyekto. Ngunit ngayon ang lahat ng code ay magkakaugnay, kaya kung magsulat ka ng isang bagong DLL na naglalaman ng DirectX code, kakailanganin mong muling i-compile ang maipapatupad na file din. Ang tanging kaginhawahan ay hindi mo kailangang mag-alala tungkol sa paglo-load (bagaman kung ito ay isang kaginhawaan ay hindi alam kung nilo-load mo ang parehong mga DLL, kumukuha ng memorya, at kailangan lang ng isa sa mga ito). Sa wakas, sa palagay ko, ang pinakamagandang ideya ay hayaan ang executable na magpasya kung aling DLL ang ilo-load sa startup. Halimbawa, kung natukoy ng programa na hindi sinusuportahan ng system ang OpenGL acceleration, mas mainam na mag-load ng DLL na may DirectX code, kung hindi man ay i-load ang OpenGL. Samakatuwid, ang late linking ay nakakatipid ng memory at binabawasan ang dependency sa pagitan ng DLL at ng executable. Gayunpaman, sa kasong ito, ang isang paghihigpit ay ipinapataw sa mga na-export na bagay - ang mga C-style na function lamang ang maaaring i-export. Hindi ma-load ang mga klase at variable kung gumagamit ang program ng late binding. Tingnan natin kung paano malalampasan ang limitasyong ito gamit ang mga interface.

Ang isang DLL na idinisenyo para sa late binding ay karaniwang gumagamit ng .def file upang tukuyin ang mga bagay na gusto nitong i-export. Kung ayaw mong gumamit ng .def file, maaari mong gamitin lang ang __declspec(dllexport) prefix bago ang mga na-export na function. Parehong ginagawa ang parehong pamamaraan. Nilo-load ng kliyente ang DLL sa pamamagitan ng pagpasa ng pangalan ng DLL file sa Win32 LoadLibrary() function. Ibinabalik ng function na ito ang HINSTANCE handle, na ginagamit upang gumana sa DLL at kinakailangan upang i-unload ang DLL mula sa memory kapag wala ito mas matagal na kailangan. Pagkatapos i-load ang DLL, ang kliyente ay makakakuha ng pointer sa anumang function gamit ang GetProcAddress() function, gamit ang pangalan ng kinakailangang function bilang parameter.

//================================================================ =========================== // .def file LIBRARY myfirstdll.dll DESCRIPTION "My first DLL" EXPORTS MyFunction //=== ================= ================================== ==================== /* Pagpapatupad ng function sa DLL */ bool MyFunction(int parms) ( //do something......... ... ) //=============================== ================= //Client code /* Ang deklarasyon ng function ay talagang kailangan lang para matukoy ang mga parameter. Ang mga deklarasyon ng function ay karaniwang nasa isang header file na ibinigay kasama ng DLL. Ang extern C keyword sa isang function declaration ay nagsasabi sa compiler na gamitin ang C variable na mga convention sa pagbibigay ng pangalan. */ extern "C" bool MyFunction(int parms); typedef bool (*MYFUNCTION)(int parms); MYFUNCTION pfnMyFunc=0; //pointer to MyFunction HINSTANCE hMyDll = ::LoadLibrary("myfirstdll.dll"); if(hMyDll != NULL) ( //Tukuyin ang address ng function na pfnMyFunc= (MYFUNCTION)::GetProcAddress(hMyDll, "MyFunction"); //Kung hindi matagumpay, i-unload ang DLL if(pfnMyFunc== 0) ( :: FreeLibrary(hMyDll) ; return; ) //Tawagan ang function bool result = pfnMyFunc(parms); //I-unload ang DLL kung hindi na natin ito kailangan::FreeLibrary(hMyDll); )

Tulad ng nakikita mo, ang code ay medyo straight forward. Ngayon tingnan natin kung paano maipapatupad ang pagtatrabaho sa "mga klase." Gaya ng nasabi kanina, kung gagamitin ang late binding, walang direktang paraan para mag-import ng mga klase mula sa DLL, kaya kailangan nating ipatupad ang "functionality" ng klase gamit ang isang interface na naglalaman ng lahat ng pampublikong function, hindi kasama ang constructor at destructor. Ang interface ay magiging isang regular na istraktura ng C/C++ na naglalaman lamang ng mga virtual abstract na function ng miyembro. Ang aktwal na klase sa DLL ay magmamana mula sa istrukturang ito at ipapatupad ang lahat ng mga function na tinukoy sa interface. Ngayon, para ma-access ang klase na ito mula sa isang client application, ang kailangan lang nating gawin ay i-export ang C-style na mga function na naaayon sa class instance at itali ang mga ito sa interface na ating tinukoy para magamit ng kliyente ang mga ito. Upang maipatupad ang gayong pamamaraan, kailangan namin ng dalawa pang pag-andar, ang isa ay lilikha ng interface, at ang pangalawa ay tatanggalin ang interface pagkatapos naming magtrabaho dito. Ang isang halimbawa ng pagpapatupad ng ideyang ito ay ibinigay sa ibaba.

//================================================================ =========================== // .def file LIBRARY myinterface.dll DESCRIPTION "nagpapatupad ng interface I_MyInterface EXPORTS GetMyInterface FreeMyInterface //== === ================================================ ======= // Header file na ginamit sa Dll at client // na nagdedeklara ng interface // I_MyInterface.h struct I_MyInterface ( virtual bool Init(int parms)=0; virtual bool Release()=0; virtual void DoStuff() =0; ); /* Mga deklarasyon ng na-export na mga function ng Dll at pagtukoy ng mga uri ng pointer ng function para sa madaling pag-load at pagtatrabaho sa mga function. Pansinin ang extern na prefix na "C", na nagsasabi sa compiler na ginagamit ang mga function na C-style */ extern "C" ( HRESULT GetMyInterface(I_MyInterface ** pInterface); typedef HRESULT (*GETINTERFACE)(I_MyInterface ** pInterface); HRESULT FreeMyInterface(I_MyInterface ** pInterface); typedef HRESULT (*FREEINTERFACE)(I_Interface) **I_Interface //========================================= ============ // Pagpapatupad ng interface sa Dll // MyInterface.h class CMyClass: public I_MyInterface ( public: bool Init(int parms); bool Release(); void DoStuff(); CMyClass(); ~CMyClass(); //alinmang ibang miyembro ng klase............ pribado: //alinmang miyembro ng klase............); //================================================================ =========================== // Mga na-export na function na lumilikha at sumisira ng interface // Dllmain.h HRESULT GetMyInterface(I_MyInterface ** pInterface ) ( kung(!*pInterface) ( *pInterface= bagong CMyClass; ibalik ang S_OK; ) ibalik ang E_FAIL; ) HRESULT FreeMyInterface(I_MyInterface ** pInterface) ( kung(!*pInterface) ibalik ang E_FAIL; tanggalin ang *pInterface; *pInterface= 0; ibalik ang S_OK; ) //================================== =================== // Code ng kliyente //Mga deklarasyon ng interface at tawag sa function GETINTERFACE pfnInterface=0;//pointer sa function na GetMyInterface I_MyInterface * pInterface =0;// pointer sa istraktura ng MyInterface HINSTANCE hMyDll = ::LoadLibrary("myinterface.dll"); if(hMyDll != NULL) ( //Tukuyin ang address ng function na pfnInterface= (GETINTERFACE)::GetProcAddress(hMyDll, "GetMyInterface"); //I-unload ang DLL kung nabigo ang nakaraang operasyon kung(pfnInterface == 0) ( ::FreeLibrary (hMyDll); return; ) //Tawagan ang function na HRESULT hr = pfnInterface(&pInterface); //I-unload kung hindi matagumpay kung(FAILED(hr)) ( ::FreeLibrary(hMyDll); return; ) //Ang interface ay na-load, maaari mong tawagan ang mga function na pInterface->Init(1); pInterface->DoStuff(); pInterface->Release(); //Bitawan ang interface FREEINTERFACE pfnFree = (FREEINTERFACE)::GetProcAddress(hMyDll,"FreeMyInterface") ; kung(pfnLibre ! = 0) pfnFree(&hMyDll); //Unload DLL::FreeLibrary(hMyDll); )

Ang impormasyong ito ay sapat na para maramdaman mo ang lahat ng kaginhawahan ng paggamit ng mga interface. Maligayang programming!