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.
Wanneer u een DLL (Dynamic Link Library) bouwt met Visual Studio, bevat de linker standaard de Visual C++ runtimebibliotheek (VCRuntime). VcRuntime bevat code die is vereist voor het initialiseren en beëindigen van een uitvoerbaar C/C++-bestand. Wanneer deze is gekoppeld aan een DLL, biedt de VCRuntime-code een interne DLL-entrypointfunctie _DllMainCRTStartup die berichten van het Windows-besturingssysteem verwerkt om de DLL te koppelen aan of los te koppelen van een proces of thread. De _DllMainCRTStartup functie voert essentiële taken uit, zoals het opzetten van stackbufferbeveiliging, de initialisatie en afsluiting van de C-runtimebibliotheek (CRT), en het aanroepen van constructors en destructors voor statische en globale objecten.
_DllMainCRTStartup roept ook hook-functies aan voor andere bibliotheken, zoals WinRT, MFC en ATL, om hun eigen initialisatie en beëindiging uit te voeren. Zonder deze initialisatie worden de CRT en andere bibliotheken, evenals uw statische variabelen, in een niet-geïnitialiseerde status achtergelaten. Dezelfde interne initialisatie- en beëindigingsroutines van VCRuntime worden aangeroepen of uw DLL gebruikmaakt van een statisch gekoppelde CRT of een dynamisch gekoppelde CRT-DLL.
Standaard-DLL-invoerpunt _DllMainCRTStartup
In Windows kunnen alle DLL's een optionele invoerpuntfunctie bevatten, meestal DllMain genoemd, die wordt aangeroepen voor zowel initialisering als beëindiging. Dit biedt u de mogelijkheid om zo nodig extra resources toe te wijzen of vrij te geven. Windows roept de invoerpuntfunctie aan in vier situaties: proceskoppeling, procesontkoppeling, threadkoppeling en threadontkoppeling. Wanneer een DLL in een procesadresruimte wordt geladen, hetzij wanneer een toepassing die deze gebruikt, wordt geladen of wanneer de toepassing de DLL tijdens runtime aanvraagt, maakt het besturingssysteem een afzonderlijke kopie van de DLL-gegevens. Dit wordt procesaanhechting genoemd.
Threadkoppeling vindt plaats wanneer het proces waarin het DLL-bestand wordt geladen, een nieuwe thread maakt.
Thread loskoppelen treedt op wanneer de thread wordt beëindigd en proces loskoppelen wanneer het DLL-bestand niet meer vereist is en wordt vrijgegeven door een toepassing. Het besturingssysteem roept het DLL-toegangspunt voor elk van deze gebeurtenissen afzonderlijk aan, waarbij een redenargument wordt doorgegeven voor elk gebeurtenistype. Het besturingssysteem verzendt DLL_PROCESS_ATTACH bijvoorbeeld als het redenargument om proceskoppeling te signaleren.
De VCRuntime-bibliotheek biedt een invoerpuntfunctie die wordt aangeroepen _DllMainCRTStartup voor het afhandelen van standaardinitialisatie- en beëindigingsbewerkingen. Bij proceskoppeling stelt de _DllMainCRTStartup functie bufferbeveiligingscontroles in, initialiseert de CRT en andere bibliotheken, initialiseert de runtimetypegegevens, initialiseert en roept constructors aan voor statische en niet-lokale gegevens, initialiseert thread-lokale opslag, wordt een interne statische teller voor elke bijlage verhoogd en wordt vervolgens een door de gebruiker of bibliotheek opgegeven DllMainaangeroepen. Bij het loskoppelen van het proces doorloopt de functie deze stappen omgekeerd. Het roept DllMain aan, vermindert de interne teller, roept destructors aan, roept CRT-beëindigingsfuncties en geregistreerde atexit functies aan en informeert eventuele andere bibliotheken over beëindiging. Wanneer de bijlageteller naar nul gaat, retourneert de functie FALSE om aan Windows aan te geven dat de DLL kan worden ontladen. De _DllMainCRTStartup functie wordt ook aangeroepen tijdens het koppelen van threads en het loskoppelen van threads. In deze gevallen voert de VCRuntime-code geen extra initialisatie of beëindiging zelf uit en roept alleen aan DllMain om het bericht door te geven. Als DllMainFALSE retourneert van proceskoppeling, waarmee een fout wordt gesignaleerd, roept _DllMainCRTStartup opnieuw DllMain aan en geeft DLL_PROCESS_DETACH door als het redenargument, waarna het verdere beëindigingsproces wordt doorlopen.
Bij het bouwen van DLL's in Visual Studio wordt het standaardinvoerpunt _DllMainCRTStartup dat door VCRuntime wordt geleverd, automatisch gekoppeld. U hoeft geen invoerpuntfunctie voor uw DLL op te geven met behulp van de linkeroptie /ENTRY (Invoerpuntsymbool).
Opmerking
Hoewel het mogelijk is om een andere invoerpuntfunctie voor een DLL op te geven met behulp van de optie /ENTRY: linker, raden we dit niet aan, omdat uw invoerpuntfunctie alles moet dupliceren dat _DllMainCRTStartup wel in dezelfde volgorde gebeurt. De VCRuntime biedt functies waarmee u het gedrag ervan kunt dupliceren. U kunt bijvoorbeeld __security_init_cookie direct bellen bij proceskoppeling ter ondersteuning van de buffercontroleoptie /GS (Bufferbeveiligingscontrole ). U kunt de _CRT_INIT functie aanroepen, waarbij dezelfde parameters worden doorgegeven als de invoerpuntfunctie, om de rest van de DLL-initialisatie- of beëindigingsfuncties uit te voeren.
Een DLL initialiseren
Uw DLL heeft mogelijk initialisatiecode die moet worden uitgevoerd wanneer uw DLL wordt geladen. Als u uw eigen DLL-initialisatie- en beëindigingsfuncties wilt uitvoeren, _DllMainCRTStartup roept u een functie DllMain aan die u kunt opgeven. U DllMain moet beschikken over de handtekening die is vereist voor een DLL-invoerpunt. De standaardinvoerpuntfunctie _DllMainCRTStartup roept DllMain dezelfde parameters aan die door Windows worden doorgegeven. Als u standaard geen functie opgeeft, zorgt Visual Studio ervoor dat er een voor u is en deze wordt gekoppeld zodat DllMain altijd iets heeft om aan te roepen. Dit betekent dat als u uw DLL niet hoeft te initialiseren, er niets speciaals is dat u hoeft te doen bij het bouwen van uw DLL.
Dit is de handtekening die wordt gebruikt voor DllMain:
#include <windows.h>
extern "C" BOOL WINAPI DllMain (
HINSTANCE const instance, // handle to DLL module
DWORD const reason, // reason for calling function
LPVOID const reserved); // reserved
Sommige bibliotheken verpakken de DllMain functie voor u. Implementeer bijvoorbeeld in een reguliere MFC-DLL de functies van CWinApp het InitInstance object en ExitInstance lid om initialisatie en beëindiging uit te voeren die vereist zijn voor uw DLL. Zie de sectie Reguliere MFC-DLL's initialiseren voor meer informatie.
Waarschuwing
Er zijn aanzienlijke limieten voor wat u veilig kunt doen in een DLL-toegangspunt. Zie DllMain voor meer informatie over specifieke Windows-API's die onveilig zijn om in te bellen. Als u iets anders nodig hebt dan de eenvoudigste initialisatie, doet u dat in een initialisatiefunctie voor het DLL-bestand. U kunt vereisen dat toepassingen de initialisatiefunctie aanroepen nadat DllMain deze is uitgevoerd en voordat ze andere functies in het DLL-bestand aanroepen.
Gewone DLL's die niet-MFC zijn initialiseren
Als u uw eigen initialisatie wilt uitvoeren in gewone (niet-MFC)-DLL's die gebruikmaken van het door VCRuntime geleverde _DllMainCRTStartup toegangspunt, moet uw DLL-broncode een functie bevatten met de naam DllMain. De volgende code geeft een basisskelet weer waarin wordt getoond hoe de definitie DllMain eruit kan zien:
#include <windows.h>
extern "C" BOOL WINAPI DllMain (
HINSTANCE const instance, // handle to DLL module
DWORD const reason, // reason for calling function
LPVOID const reserved) // reserved
{
// Perform actions based on the reason for calling.
switch (reason)
{
case DLL_PROCESS_ATTACH:
// Initialize once for each new process.
// Return FALSE to fail DLL load.
break;
case DLL_THREAD_ATTACH:
// Do thread-specific initialization.
break;
case DLL_THREAD_DETACH:
// Do thread-specific cleanup.
break;
case DLL_PROCESS_DETACH:
// Perform any necessary cleanup.
break;
}
return TRUE; // Successful DLL_PROCESS_ATTACH.
}
Opmerking
Oudere Windows SDK-documentatie geeft aan dat de werkelijke naam van de DLL-invoerpuntfunctie moet worden opgegeven op de linker-opdrachtregel met de optie /ENTRY. Met Visual Studio hoeft u de optie /ENTRY niet te gebruiken als de naam van uw invoerpuntfunctie is DllMain. Als u de optie /ENTRY gebruikt en uw invoerpuntfunctie een andere naam geeft dan DllMain, wordt de CRT niet correct geïnitialiseerd, tenzij uw invoerpuntfunctie dezelfde initialisatie-aanroepen uitvoert die _DllMainCRTStartup worden uitgevoerd.
Standaard MFC-DLLs initialiseren
Omdat reguliere MFC-DLL's een CWinApp object hebben, moeten ze hun initialisatie- en beëindigingstaken uitvoeren op dezelfde locatie als een MFC-toepassing: in de InitInstance functies en ExitInstance lidfuncties van de afgeleide klasse van het DLL-bestand CWinApp. Omdat MFC een DllMain functie biedt die wordt aangeroepen door _DllMainCRTStartupDLL_PROCESS_ATTACH enDLL_PROCESS_DETACH, moet u uw eigen DllMain functie niet schrijven. De door MFC geleverde DllMain functie roept InitInstance aan wanneer uw DLL wordt geladen en roept ExitInstance aan voordat het DLL-bestand wordt geladen.
Een reguliere MFC DLL kan meerdere threads bijhouden door TlsAlloc en TlsGetValue aan te roepen in de InitInstance functie. Met deze functies kan het DLL-bestand threadspecifieke gegevens bijhouden.
In uw reguliere MFC-DLL die dynamisch aan MFC is gekoppeld, worden, indien u respectievelijk MFC OLE, MFC-database (of DAO) of MFC Sockets-ondersteuning gebruikt, de debug-MFC-extensie-DLL's MFCOversieD.dll, MFCDversieD.dllen MFCNversieD.dll (waarbij versie het versienummer is) automatisch gekoppeld. U moet een van de volgende vooraf gedefinieerde initialisatiefuncties aanroepen voor elk van deze DLL's die u gebruikt in de reguliere MFC DLL's CWinApp::InitInstance.
| Type van MFC-ondersteuning | Initialisatiefunctie die moet worden aangeroepen |
|---|---|
| MFC OLE (MFCO-versieD.dll) | AfxOleInitModule |
| MFC Database (MFCD-versieD.dll) | AfxDbInitModule |
| MFC Sockets (MFCN-versieD.dll) | AfxNetInitModule |
MFC-extensie-DLL's initialiseren
Omdat MFC-extension-DLL's geen door CWinApp afgeleid object hebben (zoals wat reguliere MFC-DLL's doen), moet u uw initialisatie- en terminatiecode toevoegen aan de DllMain functie die de MFC DLL-wizard genereert.
De wizard bevat de volgende code voor MFC-extensie-DLL's. In de code PROJNAME staat een tijdelijke aanduiding voor de naam van uw project.
#include "pch.h" // For Visual Studio 2017 and earlier, use "stdafx.h"
#include <afxdllx.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
static AFX_EXTENSION_MODULE PROJNAMEDLL;
extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
TRACE0("PROJNAME.DLL Initializing!\n");
// MFC extension DLL one-time initialization
AfxInitExtensionModule(PROJNAMEDLL,
hInstance);
// Insert this DLL into the resource chain
new CDynLinkLibrary(Dll3DLL);
}
else if (dwReason == DLL_PROCESS_DETACH)
{
TRACE0("PROJNAME.DLL Terminating!\n");
}
return 1; // ok
}
Tijdens de initialisatie maakt u een nieuw CDynLinkLibrary-object, zodat de MFC-extensies DLL objecten of bronnen CRuntimeClass naar de clienttoepassing kan exporteren.
Als u de DLL van de MFC-extensie uit een of meer reguliere MFC-DLL's gaat gebruiken, moet u een initialisatiefunctie exporteren waarmee een CDynLinkLibrary object wordt gemaakt. Deze functie moet worden aangeroepen vanuit elk van de reguliere MFC-DLL's die gebruikmaken van de MFC-extensie-DLL. Een geschikte plaats om deze initialisatiefunctie aan te roepen, bevindt zich in de InitInstance lidfunctie van het reguliere MFC DLL-object CWinAppdat is afgeleid voordat u een van de geëxporteerde klassen of functies van de MFC-extensie DLL gebruikt.
In de DllMain die de MFC DLL Wizard genereert, legt de aanroep naar AfxInitExtensionModule de runtime-klassen (CRuntimeClass structuren) van de module vast, evenals de object-factories (COleObjectFactory objecten), voor gebruik wanneer het CDynLinkLibrary object wordt gemaakt. Controleer de retourwaarde van AfxInitExtensionModule; als een nulwaarde wordt geretourneerd van AfxInitExtensionModule, retourneer dan nul van uw DllMain functie.
Als uw MFC-extensie-DLL expliciet wordt gelinkt aan een uitvoerbaar bestand (wat betekent dat het uitvoerbare bestand AfxLoadLibrary aanroept om een koppeling naar de DLL te maken), moet u een aanroep toevoegen aan AfxTermExtensionModuleDLL_PROCESS_DETACH. Met deze functie kan MFC het DLL-bestand van de MFC-extensie opschonen wanneer elk proces loskoppelt van de MFC-extensie-DLL (wat gebeurt wanneer het proces wordt afgesloten of wanneer het DLL-bestand wordt verwijderd als gevolg van een AfxFreeLibrary aanroep). Als uw MFC-extensie-DLL impliciet wordt gekoppeld aan de toepassing, is de aanroep naar AfxTermExtensionModule niet nodig.
Toepassingen die expliciet zijn gekoppeld aan MFC-extensie-DLL's moeten AfxTermExtensionModule aanroepen bij het vrijmaken van de DLL. Ze moeten ook gebruikmaken van AfxLoadLibrary en AfxFreeLibrary (in plaats van de Win32-functies LoadLibrary en FreeLibrary) als de toepassing meerdere threads gebruikt. Het gebruik AfxLoadLibrary en AfxFreeLibrary zorgt ervoor dat de opstart- en afsluitcode die wordt uitgevoerd wanneer de DLL van de MFC-extensie wordt geladen en verwijderd, de globale MFC-status niet beschadigd.
Omdat de MFCx0.dll volledig is geïnitialiseerd door de tijd DllMain wordt aangeroepen, kunt u geheugen toewijzen en MFC-functies aanroepen binnen DllMain (in tegenstelling tot de 16-bits versie van MFC).
Extensie-DLL's kunnen voor multithreading zorgen door de cases DLL_THREAD_ATTACH en DLL_THREAD_DETACH in de functie DllMain te verwerken. Deze gevallen worden toegewezen aan DllMain wanneer threads worden gekoppeld en losgekoppeld van de DLL.
Door TlsAlloc aan te roepen wanneer een DLL wordt gekoppeld, kan het DLL-bestand lokale opslagindexen (Thread Local Storage) onderhouden voor elke thread die aan het DLL-bestand is gekoppeld.
Houd er rekening mee dat het headerbestand Afxdllx.h speciale definities bevat voor structuren die worden gebruikt in MFC-extensie-DLL's, zoals de definitie voor AFX_EXTENSION_MODULE en CDynLinkLibrary. Neem dit headerbestand op in het DLL-bestand met de MFC-extensie.
Opmerking
Het is belangrijk dat u geen van de _AFX_NO_XXX macro's in pch.h (stdafx.h in Visual Studio 2017 en eerder) definieert of ongedaan maakt. Deze macro's bestaan alleen om te controleren of een bepaald doelplatform die functie ondersteunt of niet. U kunt uw programma schrijven om deze macro's te controleren (bijvoorbeeld #ifndef _AFX_NO_OLE_SUPPORT), maar uw programma mag deze macro's nooit definiëren of ongedaan maken.
Een voorbeeld van een initialisatiefunctie die multithreading verwerkt, is opgenomen in Het gebruik van lokale threadopslag in een Dynamic-Link-bibliotheek in de Windows SDK. Houd er rekening mee dat het voorbeeld een invoerpuntfunctie bevat die wordt aangeroepen LibMain, maar u moet deze functie DllMain een naam opgeven zodat deze werkt met de MFC- en C-runtimebibliotheken.
Zie ook
C/C++-DLL's maken in Visual Studio
DllMain-toegangspunt
Beste praktijken voor Dynamische Koppelingsbibliotheek