Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Een uitvoerbaar bestand koppelt (of laadt) een DLL op twee manieren:
Impliciete koppeling, waarbij het besturingssysteem het DLL-bestand tegelijkertijd laadt als het uitvoerbare bestand dat het gebruikt. Het uitvoerbare bestand van de client roept de geëxporteerde functies van het DLL-bestand op dezelfde manier aan als als de functies statisch zijn gekoppeld en in het uitvoerbare bestand zijn opgenomen. Impliciete koppeling wordt soms ook wel dynamische koppeling van statische belasting of laadtijd genoemd.
Expliciete koppeling, waarbij het besturingssysteem de DLL op aanvraag laadt tijdens runtime. Een uitvoerbaar bestand dat gebruikmaakt van een DLL door expliciete koppeling te maken, moet het DLL-bestand expliciet laden en loskoppelen. Er moet ook een functiepointer worden ingesteld voor toegang tot elke functie die wordt gebruikt vanuit het DLL-bestand. In tegenstelling tot aanroepen naar functies in een statisch gekoppelde bibliotheek of een impliciet gekoppeld DLL-bestand, moet het uitvoerbare clientbestand de geëxporteerde functies aanroepen in een expliciet gekoppeld DLL-bestand via functieaanwijzers. Expliciete koppeling wordt soms ook wel dynamische belasting of dynamische koppeling van runtime genoemd.
Een uitvoerbaar bestand kan een koppelingsmethode gebruiken om een koppeling naar hetzelfde DLL-bestand te maken. Bovendien sluiten deze methoden elkaar niet uit; een uitvoerbaar bestand kan impliciet een koppeling maken naar een DLL en een andere kan er expliciet aan worden gekoppeld.
Bepalen welke koppelingsmethode moet worden gebruikt
Of u impliciete koppeling of expliciete koppeling wilt gebruiken, is een architectuurbeslissing die u moet nemen voor uw toepassing. Er zijn voor- en nadelen voor elke methode.
Impliciete koppeling
Impliciete koppeling vindt plaats wanneer de code van een toepassing een geëxporteerde DLL-functie aanroept. Wanneer de broncode voor het aanroepende uitvoerbare bestand wordt gecompileerd of geassembleerd, genereert de DLL-functieaanroep een externe functieverwijzing in de objectcode. Als u deze externe verwijzing wilt oplossen, moet de toepassing een koppeling maken met de importbibliotheek (.lib-bestand) die is geleverd door de maker van het DLL-bestand.
De importbibliotheek bevat alleen code voor het laden van het DLL-bestand en het implementeren van aanroepen naar functies in het DLL-bestand. Het zoeken van een externe functie in een importbibliotheek informeert de linker dat de code voor die functie zich in een DLL bevindt. Als u externe verwijzingen naar DLL's wilt oplossen, voegt de linker gewoon informatie toe aan het uitvoerbare bestand dat aangeeft waar de DLL-code moet worden gevonden wanneer het proces wordt gestart.
Wanneer het systeem een programma start dat dynamisch gekoppelde verwijzingen bevat, wordt de informatie in het uitvoerbare bestand van het programma gebruikt om de vereiste DLL's te vinden. Als het DLL-bestand niet kan worden gevonden, beëindigt het systeem het proces en wordt een dialoogvenster weergegeven waarin de fout wordt gerapporteerd. Anders wijst het systeem de DLL-modules toe aan de adresruimte van het proces.
Als een van de DLL's een ingangspuntfunctie heeft voor initialisatie en beëindigingscode zoals DllMain
, roept het besturingssysteem de functie aan. Een van de parameters die worden doorgegeven aan de invoerpuntfunctie, geeft een code op die aangeeft dat het DLL-bestand wordt gekoppeld aan het proces. Als de entry-point functie niet TRUE retourneert, beëindigt het systeem het proces en geeft het systeem de foutmelding.
Ten slotte wijzigt het systeem de uitvoerbare code van het proces om beginadressen voor de DLL-functies op te geven.
Net als de rest van de code van een programma wijst het laadprogramma DLL-code toe aan de adresruimte van het proces wanneer het proces wordt gestart. Het besturingssysteem laadt het alleen in het geheugen wanneer dat nodig is. Als gevolg hiervan hebben de PRELOAD
en LOADONCALL
codekenmerken die door .def-bestanden worden gebruikt om het laden in eerdere versies van Windows te beheren, geen betekenis meer.
Expliciete koppeling
De meeste toepassingen gebruiken impliciete koppelingen, omdat dit de eenvoudigste manier is om te gebruiken. Er zijn echter momenten waarop expliciete koppeling nodig is. Hier volgen enkele veelvoorkomende redenen om expliciete koppelingen te gebruiken:
De toepassing weet de naam van een DLL die het laadt pas tijdens de uitvoering. De toepassing kan bijvoorbeeld de naam van het DLL-bestand en de geëxporteerde functies ophalen uit een configuratiebestand bij het opstarten.
Een proces dat impliciet koppelen gebruikt, wordt beëindigd door het besturingssysteem als het DLL-bestand niet wordt gevonden bij het opstarten van het proces. Een proces dat gebruikmaakt van expliciete koppeling, wordt in deze situatie niet beëindigd en kan proberen om te herstellen van de fout. Het proces kan bijvoorbeeld de gebruiker op de hoogte stellen van de fout en de gebruiker een ander pad naar het DLL-bestand laten opgeven.
Een proces dat gebruikmaakt van impliciete koppeling wordt ook beëindigd als een van de DLL's waarmee het verbonden is een
DllMain
functie bevat die faalt. Een proces dat gebruikmaakt van expliciete koppeling, wordt in deze situatie niet beëindigd.Een toepassing die impliciet koppelingen naar veel DLL's maakt, kan traag worden gestart omdat Windows alle DLL's laadt wanneer de toepassing wordt geladen. Om de opstartprestaties te verbeteren, kan een toepassing alleen impliciete koppeling gebruiken voor DLL's die direct na het laden zijn vereist. Mogelijk wordt expliciete koppeling gebruikt om alleen andere DLL's te laden wanneer ze nodig zijn.
Expliciete koppeling elimineert de noodzaak om de toepassing te koppelen met behulp van een importbibliotheek. Als wijzigingen in het DLL-bestand ertoe leiden dat de exportdinalen worden gewijzigd, hoeven toepassingen niet opnieuw te koppelen als ze de naam van een functie aanroepen
GetProcAddress
en niet een rangtelwaarde. Toepassingen die impliciete koppeling gebruiken, moeten nog steeds opnieuw worden gekoppeld aan de gewijzigde importbibliotheek.
Hier volgen twee gevaren voor expliciete koppeling om rekening mee te houden:
Als het DLL-bestand een
DllMain
ingangspuntfunctie heeft, roept het besturingssysteem de functie aan in de context van de thread die wordt aangeroepenLoadLibrary
. De invoerpuntfunctie wordt niet aangeroepen als het DLL-bestand al aan het proces is gekoppeld vanwege een eerdere aanroep naarLoadLibrary
die geen overeenkomende aanroep van deFreeLibrary
functie heeft gehad. Expliciete koppeling kan problemen veroorzaken als het DLL-bestand eenDllMain
functie gebruikt om elke thread van een proces te initialiseren, omdat threads die al bestaan wanneerLoadLibrary
(ofAfxLoadLibrary
) wordt aangeroepen, niet worden geïnitialiseerd.Als een DLL statische gegevens declareert zoals
__declspec(thread)
, kan dit een beveiligingsfout veroorzaken als deze expliciet is gekoppeld. Nadat het DLL-bestand is geladen door een aanroep naarLoadLibrary
, veroorzaakt het een beveiligingsfout wanneer de code naar deze gegevens verwijst. (Statische-omvanggegevens omvatten zowel globale als lokale statische items.) Daarom moet u, wanneer u een DLL maakt, voorkomen dat u thread-lokale opslag gebruikt. Als u dat niet kunt, informeert u uw DLL-gebruikers over de mogelijke valkuilen van het dynamisch laden van uw DLL. Zie Het gebruik van lokale threadopslag in een Dynamic Link Library (Windows SDK) voor meer informatie.
Impliciete koppeling gebruiken
Als u een DLL wilt gebruiken door impliciet koppelen, moeten uitvoerbare clientbestanden deze bestanden verkrijgen van de provider van het DLL-bestand:
Een of meer headerbestanden (.h-bestanden) die de declaraties van de geëxporteerde gegevens, functies en C++-klassen in het DLL-bestand bevatten. De klassen, functies en gegevens die door het DLL-bestand worden geëxporteerd, moeten allemaal worden gemarkeerd
__declspec(dllimport)
in het headerbestand. Zie dllexport, dllimport voor meer informatie.Een importbibliotheek die u kunt koppelen aan uw uitvoerbare bestand. De linker creëert de importbibliotheek wanneer de DLL wordt gebouwd. Zie LIB-bestanden als linkerinvoer voor meer informatie.
Het werkelijke DLL-bestand.
Als u de gegevens, functies en klassen in een DLL wilt gebruiken door impliciete koppeling te maken, moet elk clientbronbestand de headerbestanden bevatten die ze declareren. Vanuit een coderingsperspectief zijn aanroepen naar de geëxporteerde functies net als elke andere functieoproep.
Als u het uitvoerbare bestand van de client wilt maken, moet u een koppeling maken met de importbibliotheek van het DLL-bestand. Als u een extern makefile- of buildsysteem gebruikt, geeft u de importbibliotheek op samen met de andere objectbestanden of bibliotheken die u koppelt.
Het besturingssysteem moet het DLL-bestand kunnen vinden wanneer het aanroepende uitvoerbare bestand wordt geladen. Dit betekent dat u het bestaan van het DLL-bestand moet implementeren of controleren wanneer u uw toepassing installeert.
Expliciet koppelen aan een DLL
Als u een DLL wilt gebruiken door expliciet te koppelen, moeten toepassingen een functie-aanroep uitvoeren om het DLL-bestand expliciet te laden tijdens runtime. Als u expliciet een koppeling wilt maken naar een DLL, moet een toepassing het volgende doen:
Roep LoadLibraryEx of een vergelijkbare functie aan om het DLL-bestand te laden en een modulehandgreep te verkrijgen.
Roep GetProcAddress aan om een functieaanwijzer te verkrijgen voor elke geëxporteerde functie die door de toepassing wordt aangeroepen. Omdat toepassingen de DLL-functies aanroepen via een aanwijzer, genereert de compiler geen externe verwijzingen, dus er is geen koppeling met een importbibliotheek nodig. U moet echter een
typedef
ofusing
instructie hebben waarmee de aanroephandtekening wordt gedefinieerd van de geëxporteerde functies die u aanroept.Roep FreeLibrary aan wanneer u klaar bent met het DLL-bestand.
Met deze voorbeeldfunctie wordt bijvoorbeeld een DLL met de naam 'MyDLL' aangeroepen LoadLibrary
, wordt aangeroepen GetProcAddress
om een aanwijzer te verkrijgen naar een functie met de naam DLLFunc1, wordt de functie aangeroepen en wordt het resultaat opgeslagen en wordt vervolgens aangeroepen FreeLibrary
om het DLL-bestand te ontladen.
#include "windows.h"
typedef HRESULT (CALLBACK* LPFNDLLFUNC1)(DWORD,UINT*);
HRESULT LoadAndCallSomeFunction(DWORD dwParam1, UINT * puParam2)
{
HINSTANCE hDLL; // Handle to DLL
LPFNDLLFUNC1 lpfnDllFunc1; // Function pointer
HRESULT hrReturnVal;
hDLL = LoadLibrary("MyDLL");
if (NULL != hDLL)
{
lpfnDllFunc1 = (LPFNDLLFUNC1)GetProcAddress(hDLL, "DLLFunc1");
if (NULL != lpfnDllFunc1)
{
// call the function
hrReturnVal = lpfnDllFunc1(dwParam1, puParam2);
}
else
{
// report the error
hrReturnVal = ERROR_DELAY_LOAD_FAILED;
}
FreeLibrary(hDLL);
}
else
{
hrReturnVal = ERROR_DELAY_LOAD_FAILED;
}
return hrReturnVal;
}
In tegenstelling tot dit voorbeeld moet u in de meeste gevallen slechts één keer in uw toepassing aanroepen LoadLibrary
FreeLibrary
voor een bepaalde DLL. Het is vooral waar als u meerdere functies in het DLL-bestand aanroept of dll-functies herhaaldelijk aanroept.