A DLL-ek alap programegységek a Windows-ban. A DLL-ek nagy szerepet játszanak abban, hogy a párhuzamosan futó programok a memóriában egyetlen egy példányban tárolt függvénycsoporton osztozkodjanak. A Windows főleg DLL-ekből áll, amelyek biztosítják az összes Windows szolgáltatást (felhasználói felület stb..).
Mint programozók biztos, hogy tisztában vagyunk a Library ("könyvtár") fogalommal. Az applikációk meghívhatják a Library függvényeit. Ahhoz, hogy az applikáció meg tudja hívni a függvényeket a könyvtárból, linkelni kell ezt a könyvtárat a programhoz. A linkelés kétféleképpen történhet
n statikus linkelés
n dinamikus linkelés
A DLL-ek olyan könyvtárak, amelyek dinamikusan linkelődnek az applikációhoz. Általában a Windows alatt megírt programok mind a két fajta linkelést használják. A következőkkel a kétfajta linkelés közötti különbséget szeretnénk bemutatni különös tekintettel a dinamikus linkelésről, hiszen a Windows rendszerben ez nélkülözhetetlen. Azt is ki merjük jelenteni, hogy ez a fajta linkelés képezi a Windows működésének lényegét.
A statikus linkelés esetében a linker másolja az összes szükséges kódot a libraryből a futtatható fájlba. A statikus linkeléssel készített futtatható fájl önállóan is képes működni, hiszen a futtatáshoz szükséges összes kód benne van a fájlban még akkor is, ha az applikáció a könyvtár egyetlen egy rutinjára hivatkozik. Ez természetesen fölöslegesen növeli a futtatható fájl méretét. A 10. ábra szemlélteti a statikus linkelés lényegét.
Ebben az esetben ha ebből az applikációból több példány fut egyszerre, akkor annyi példányban van a memóriában az összes kód, ahány példányban fut a program.
A dinamikus linkelés flexibilisebb mint a statikus. Két módja van az implicit és az explicit. Az implicit esetben az applikáció futtatható fájlja össze van linkelve egy speciális import libraryvel, ami biztosítja az interface-t a program és a DLL között. Az import library-t létre lehet hozni az IMPLIB segédprogram segítségével. A következőképen:
IMPLIB DLLNEV.LIB DLLNEV.DLL
Az így keletkezett Lib kiterjesztésű fájlt hozzá kell linkelni a programunkhoz. Ez kiváltható a definíciós fájl IMPORTS szekcióban levő függvény felsorolással. A program indításakor a Windows automatikusan tölti be a memóriába azokat a DLL-eket, amelyekre hivatkozik a programunk. A betöltött DLL-ek egyetlen egy példányban vannak jelen a memóriában attól függetlenül, hogy hány applikáció, illetve hány applikáció példány hivatkozik rájuk. A 11. ábra demonstrálja az impliciten dinamikusan linkelt futtatható fájl szerkezetét.
A 12 ábra olyan példát mutat, amikor két program fut egyszerre és azok egyetlen egy DLL-t használnak.
Explicit linkelés esetén az applikáció expliciten meghívja a LoadLibrary API függvényt, hogy betöltse a DLL-t a memóriába. A betöltés után az applikáció a DLL bármelyik függvényét el tudja érni. Egy függvény címének az eléréséhez a GetProcAddress függvényt használjuk, amelynek argumentumaként a függvény nevét adjuk meg. Amikor a programnak már nincsen szüksége az adott DLL-re, akkor a FreeLibrary függvény meghívásával megszakíthatja a kapcsolatot vele. Amennyiben expliciten használjuk a DLL-t, nincsen szükség az importkönyvtárra. Az explicit módszer akkor ajánlatos ha a a DLL erőforrásokat tartalmaz
hClickBar = LoadLibrary("BUTTON.DLL");
if (hClickBar < 32)
Érdemes megemlíteni, hogy a dinamikus könyvtáraknak más kiterjesztésük is lehet, de a Windows csak a .DLL kiterjesztésűeket tölti be automatikusan. A statikus linkelés esetén a Lib-ekre és obj-ekre csak a fejlesztés során van szükségünk a futtatáshoz nem kellenek. A dinamikus esetben akár impliciten, akár expliciten hivatkozik a programunk egy DLL-re, a DLL-nek a futási időben is "elérhető" helyen kell lennie. A Windows a következő sorrendben keresi a DLL-t:
n az aktuális könyvtár
n a Windows könyvtár
n a Windows\system könyvtár
n a futó program EXE fájlját tartalmazó könyvtár
n a PATH -ban felsorolt helyen
Minden esetben a header fájlt, ami a függvény prototipusát definiálja, inkludolni kell a programunkan.
A DLL betöltése után a Windows a LibEntry függvényt aktivizálja, amelynek a feladata a lokális adatterület kialakitása, majd meghívja a LibMain függvényt. A LibEntry függvénnyel csak akkor kell foglalkozni, ha speciális inicializálási lépéseket kivánunk végrehajtani.
Int CALLBACK LibMain(HINSTANCE hInstance, WORD wDataSeg, Word cbHeapSize, LPSTR lpszCmdLine)
A hInstance a könyvtármodul egyetlen példányának azonositására szolgál. A wDataSeg a DLL adatszegmensének a szelektorát tartalmmazza. A cbHeapSize pedig a definiciós fájlban levő heapmérettel azonos.
A Windows azokat az automatikusan betöltött DLL-könyvtárakat, amelyekre semmilyen applikáció sem hivatkozik, kitörli a memóriából és szabaddá teszi az általuk foglalt területet. Az expliciten betöltött könyvtárakat (LoadLibrary) a FreeLibrary függvény segitségével szabadithatjuk fel. A DLL megszüntetése előtt kerül sor a WEP függvény meghivására.
Int CALLBACK WEP (nTypeExit)
Az nTypeExit paraméter értékéből tudhatjuk meg a kidobás okát. Ez az érték lehet WEP_FREE_DLL vagy WEP_SYSTEM_EXIT, tehát a kidobás oka: vagy egyetlen egy applikáció sem hivatkozik a DLL függvényeire, vagy rendszerből való kilépés következik.
Ahhoz, hogy a DLL függvényeire tudjunk hivatkozni szükségünk van az importálásra. Ennek megvalósitására. 3 módszer kinálkozik:
Import Library (Lásd előbb)
A DLL definiciós fájljában az export szekcióban felsoroljuk az exportálásra kerülő függvények neveit, és az exe fájl definiciós fájljában az import szekcióban kell felsorolni. Például ha importálni akarjuk a STRLIB.DLL egy factorial nevű függvényét, akkor
IMPORTS
STRLIB.factorial
A DLL definiciós fájljában az export szekció
EXPORTS
factorial
....... @2
az exe fájl definiciós fájlja:
IMPORTS
factorial STRLIB.1
Az exe fájl definiciós fájljában
IMPORTS
Addfact=STRLIB.factorial
Itt szeretném felhívni a figyelmet arra, hogy a Win32 világban (Win32S, Win32C, Win32) esetén a FAR PASCAL helyett a WINAPI jelzőt kell használni. Amennyiben a DLL-t egy basic jellegű nyelven megírt programmal fogjuk meghívni, akkor a függvények deklarációja előtt be kell írni azt, hogy "extern C". Pl egy MSACCESS és FoxPro program csak akkor hajlandó végrahajtani a hívást. Aki VC++ 4.0-t használ, annak a leírásában az szerepelt, hogy nincsen szükség a definiciós fájlra. Ehelyett más megoldást kinál a VC++ 4.0. A tapasztalat azt mutatta, hogy amennyiben a VC++ 4.0-ban megirt DLL-t az MSACCESS 95-ben meg akartuk hívni, mindig hibajelzést kaptunk. Tehát érdemes akkor is definíciós fájlt használni. Amennyiben a DLL erőforrásokat is tartalmaz, akkor a LoadLibrary után betölthetjük az erőforrást a LoadXxxx függvénnyel (LoadIcon, LoadString, LoadCursor, stb...)
Példa
Irjunk olyan DLL-t, amely egyetlen egy faktoriális kiszámitó függvényt tartalmaz. Irjunk olyan programot, amely meghivja ezt a függvényt.
DLLLIBRARY
-------- ----- ------ ----- ----- -----*/
#include <windows.h>
#include <string.h>
#include "dlllib.h"
extern "C"
#pragma argsused
int FAR PASCAL LibMain (HANDLE hInstance, WORD wDataSeg, WORD wHeapSize,
LPSTR lpszCmdLine)
#pragma argsused
int FAR PASCAL _export WEP (int nParam)
unsigned long FAR PASCAL _export factorial(unsigned int szam)
; DLLLIB.DEF module definition file
LIBRARY DLLLIB
DESCRIPTION 'DLL Program '
EXETYPE WINDOWS
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE SINGLE
HEAPSIZE 1024
-------- ----- ------ ----- ----- -------------*/
#include <windows.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "dllprog.h"
#include "dlllib.h"
typedef unsigned int UINT ;
long FAR PASCAL _export WndProc (HWND, UINT, UINT, LONG) ;
long FAR PASCAL GyermekProc(HWND hDlg,unsigned iMessage,WORD wParam,LONG lParam);
char szAppName [] = "DllProg" ;
HANDLE hInst;
#pragma argsused
int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
hInst=hInstance;
hwnd=CreateWindow(szAppName, "DLL Demonstration Program",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd, nCmdShow) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg, NULL, 0, 0))
return msg.wParam ;
}
#pragma argsused
long FAR PASCAL _export WndProc (HWND hwnd, UINT message, UINT wParam,
LONG lParam)
return 0 ;
case WM_DESTROY:
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
#pragma argsused
long FAR PASCAL GyermekProc(HWND hDlg,unsigned iMessage,WORD wParam,LONG lParam)
break;
}
return 0;
; DLLPROG.DEF module definition file
NAME DLLPROG
DESCRIPTION 'Program DLLLIB DLL '
EXETYPE WINDOWS
STUB 'WINSTUB.EXE'
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
HEAPSIZE 1024
STACKSIZE 8192
IMPORTS DLLLIB.factorial
|