Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Lorsque vous générez une bibliothèque de liens dynamiques (DLL) à l’aide de Visual Studio, par défaut, l’éditeur de liens inclut la bibliothèque runtime Visual C++ (VCRuntime). VCRuntime contient le code requis pour initialiser et mettre fin à un exécutable C/C++. Lorsqu’il est lié à une DLL, le code VCRuntime fournit une fonction interne de point d’entrée DLL appelée _DllMainCRTStartup qui gère les messages du système d’exploitation Windows à la DLL à attacher ou à détacher d’un processus ou d’un thread. La _DllMainCRTStartup fonction effectue des tâches essentielles telles que la configuration de la sécurité de la mémoire tampon de pile, l’initialisation et la terminaison de la bibliothèque runtime C (CRT), ainsi que les appels aux constructeurs et aux destructeurs pour les objets statiques et globaux.
_DllMainCRTStartup appelle également des fonctions de raccordement pour d’autres bibliothèques telles que WinRT, MFC et ATL afin d’effectuer leur propre initialisation et arrêt. Sans cette initialisation, le CRT et d’autres bibliothèques, et vos variables statiques, sont laissés dans un état non initialisé. Les mêmes routines d’initialisation interne et de terminaison VCRuntime sont appelées si votre DLL utilise un CRT lié statiquement ou une DLL CRT liée dynamiquement.
Point d’entrée DLL par défaut _DllMainCRTStartup
Dans Windows, toutes les DLL peuvent contenir une fonction de point d’entrée facultative, généralement appelée DllMain, qui est appelée à la fois pour l’initialisation et l’arrêt. Cela vous donne la possibilité d’allouer ou de libérer d’autres ressources en fonction des besoins. Windows appelle la fonction de point d’entrée dans quatre situations : attachement de processus, détachement de processus, attachement de fil et détachement de fil. Lorsqu’une DLL est chargée dans un espace d’adressage de processus, soit lorsqu’une application qui l’utilise soit chargée, soit lorsque l’application demande la DLL au moment de l’exécution, le système d’exploitation crée une copie distincte des données DLL. Il s’agit de l’attachement de processus.
L’attachement de thread se produit lorsque le processus dans lequel la DLL est chargée crée un thread.
Le détachement de thread se produit lorsque le thread se termine, et le détachement de processus intervient lorsque la DLL n’est plus nécessaire et est libérée par une application. Le système d’exploitation effectue un appel distinct au point d’entrée DLL pour chacun de ces événements, en passant un argument de raison pour chaque type d’événement. Par exemple, le système d'exploitation envoie DLL_PROCESS_ATTACH comme argument de la raison pour signaler l'attachement du processus.
La bibliothèque VCRuntime fournit une fonction de point d’entrée appelée _DllMainCRTStartup pour gérer les opérations d’initialisation et de terminaison par défaut. Lors de l'attachement d'un processus, la fonction _DllMainCRTStartup configure des vérifications de sécurité des tampons, initialise le CRT et d'autres bibliothèques, initialise les informations de type au moment de l'exécution, initialise et appelle les constructeurs pour les données statiques et non locales, initialises le stockage local de thread, incrémente un compteur statique interne pour chaque attachement, puis appelle une fonction fournie par l'utilisateur ou la bibliothèque DllMain. Lors du détachement du processus, la fonction passe par ces étapes en sens inverse. Il appelle DllMain, décrémente le compteur interne, appelle les destructeurs, déclenche les fonctions de terminaison CRT et les fonctions enregistrées atexit, et notifie toutes les autres bibliothèques de la terminaison. Lorsque le compteur de pièces jointes est égal à zéro, la fonction retourne FALSE pour indiquer à Windows que la DLL peut être déchargée. La _DllMainCRTStartup fonction est également appelée pendant l’attachement de thread et le détachement de thread. Dans ces cas, le code VCRuntime n’effectue aucune autre initialisation ou arrêt seul, et il suffit d’appeler DllMain pour transmettre le message. Si DllMain retourne FALSE lors de l’attachement du processus, ce qui signale un échec, _DllMainCRTStartup appelle à nouveau DllMain et passe DLL_PROCESS_DETACH en tant que raison argument, puis suit le reste du processus de terminaison.
Lors de la création de DLL dans Visual Studio, le point _DllMainCRTStartup d’entrée par défaut fourni par VCRuntime est lié automatiquement. Vous n’avez pas besoin de spécifier une fonction de point d’entrée pour votre DLL à l’aide de l’option /ENTRY d’éditeur de liens (symbole de point d’entrée).
Remarque
Bien qu’il soit possible de spécifier une autre fonction de point d’entrée pour une DLL à l’aide de l’option /ENTRY: linker, nous ne le recommandons pas, car votre fonction de point d’entrée doit dupliquer tout ce qui _DllMainCRTStartup le fait, dans le même ordre. VCRuntime fournit des fonctions qui vous permettent de dupliquer son comportement. Par exemple, vous pouvez appeler __security_init_cookie immédiatement lors de l'attachement du processus pour prendre en charge l'option de vérification /GS de la sécurité de la mémoire tampon. Vous pouvez appeler la _CRT_INIT fonction, en passant les mêmes paramètres que la fonction de point d’entrée, pour effectuer le reste des fonctions d’initialisation ou de terminaison dll.
Initialiser une DLL
Votre DLL peut avoir du code d’initialisation qui doit s’exécuter lorsque votre DLL se charge. Pour que vous puissiez effectuer vos propres fonctions d’initialisation et de terminaison DLL, _DllMainCRTStartup appelez une fonction appelée DllMain que vous pouvez fournir. Votre DllMain doit avoir la signature requise pour un point d'entrée de DLL. La fonction _DllMainCRTStartup de point d’entrée par défaut appelle DllMain à l’aide des mêmes paramètres passés par Windows. Par défaut, si vous ne fournissez pas de DllMain de fonction, Visual Studio vous en fournit un et le relie afin que _DllMainCRTStartup puisse toujours appeler quelque chose. Cela signifie que si vous n’avez pas besoin d’initialiser votre DLL, il n’y a rien de spécial à faire lors de la génération de votre DLL.
Il s’agit de la signature utilisée pour 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
Certaines bibliothèques encapsulent la DllMain fonction pour vous. Par exemple, dans une DLL MFC régulière, implémentez les fonctions membres InitInstance et ExitInstance de l’objet CWinApp pour effectuer l’initialisation et la terminaison requises par votre DLL. Pour plus d’informations, consultez la section Initialiser les DLL MFC standard .
Avertissement
Il existe des limites significatives sur ce que vous pouvez faire en toute sécurité dans un point d’entrée DLL. Pour plus d’informations sur des API Windows spécifiques qui ne sont pas sécurisées pour appeler DllMain, consultez Les meilleures pratiques générales. Si vous avez besoin de tout sauf de l'initialisation la plus simple, faites-le dans une fonction d'initialisation pour la DLL. Vous pouvez exiger que les applications appellent la fonction d’initialisation après DllMain l’exécution et avant qu’elles n’appellent d’autres fonctions dans la DLL.
Initialiser des DLL non-MFC ordinaires
Pour effectuer votre propre initialisation dans des DLL ordinaires (non MFC) qui utilisent le point d’entrée fourni par _DllMainCRTStartup VCRuntime, votre code source DLL doit contenir une fonction appelée DllMain. Le code suivant présente un squelette de base montrant à quoi la définition de DllMain pourrait ressembler :
#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.
}
Remarque
L’ancienne documentation du Kit de développement logiciel (SDK) Windows indique que le nom réel de la fonction de point d’entrée DLL doit être spécifié sur la ligne de commande de l’éditeur de liens avec l’option /ENTRY . Avec Visual Studio, vous n’avez pas besoin d’utiliser l’option /ENTRY si le nom de votre fonction de point d’entrée est DllMain. En fait, si vous utilisez l’option /ENTRY et nommez votre fonction de point d’entrée autre que DllMain, le CRT n’est pas initialisé correctement, sauf si votre fonction de point d’entrée effectue les mêmes appels d’initialisation que ceux effectués _DllMainCRTStartup .
Initialisation CRT manuelle avec _CRT_INIT
Lors de la génération d’une DLL qui utilise l’une des bibliothèques runtime C, pour vous assurer que le CRT est correctement initialisé, soit :
- La fonction d’initialisation doit être nommée
DllMain()et le point d’entrée doit être spécifié avec l’option-entry:_DllMainCRTStartup@12éditeur de liens Il s’agit du comportement par défaut lors de la création d’une DLL (x86@12uniquement, car cette plateforme utilisestdcall) ou - Le point d’entrée de la DLL doit explicitement appeler
_CRT_INIT()lors de l'attachement et du détachement du processus. Cela s’applique uniquement si vous utilisez/NOENTRYou avez un point d’entrée personnalisé. Nous vous déconseillons d’utiliser/NOENTRYou d’utiliser un point d’entrée personnalisé, si possible, car vous devrez dupliquer tout le code d’initialisation et de terminaison qui_DllMainCRTStartupfournit, dans le même ordre.
Cela permet aux bibliothèques de runtime C d’allouer et d’initialiser correctement les données du runtime C lorsqu’un processus ou un thread est attaché à la DLL, de nettoyer correctement les données du runtime C lorsqu’un processus se détache de la DLL, et pour que les objets C++ globaux dans la DLL soient correctement construits et destructeurs.
Les exemples du Kit de développement logiciel (SDK) Win32 utilisent tous la première méthode. Reportez-vous à la référence du programmeur Win32 pour DllEntryPoint() et à la documentation Visual C++ pour DllMain().
DllMainCRTStartup() appelle _CRT_INIT() et _CRT_INIT() appelle l’application DllMain(), le cas échéant.
Si vous souhaitez utiliser la deuxième méthode et appeler vous-même le code d’initialisation CRT, au lieu d’utiliser DllMainCRTStartup() et DllMain(), il existe deux techniques :
Si vous disposez de votre propre point d’entrée DLL, procédez comme suit dans le point d’entrée :
a. Utilisez ce prototype (défini dans
process.hlorsque_DECL_DLLMAINest défini) pour_CRT_INIT():BOOL WINAPI _CRT_INIT(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved);Pour plus d'informations sur
_CRT_INIT()les valeurs de retour, consultez la documentation deDllEntryPoint, où les mêmes valeurs sont retournées.b. Appelez
_CRT_INIT()d'abord, avant queDLL_PROCESS_ATTACHouDLL_THREAD_ATTACHne soit appelée ou qu'une opération à virgule flottante ne soit effectuée.v. Appelez votre propre code d'initialisation/de terminaison/du thread.
d. Sur
DLL_PROCESS_DETACHetDLL_THREAD_DETACH, appelez_CRT_INIT()en dernier, une fois que toutes les fonctions runtime C ont été appelées et que toutes les opérations à virgule flottante sont terminées.
Veillez à transmettre à _CRT_INIT() tous les paramètres du point d'entrée ; _CRT_INIT() s'attend à recevoir ces paramètres, de sorte que leur omission pourrait entraîner un fonctionnement peu fiable (en particulier, fdwReason est requis pour déterminer si l'initialisation ou l'arrêt du processus est nécessaire).
Voici un exemple de fonction squelette pour le point d’entrée qui montre quand et comment effectuer des appels à _CRT_INIT() dans le point d’entrée de la DLL :
BOOL WINAPI DllEntryPoint(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
{
if (fdwReason == DLL_PROCESS_ATTACH || fdwReason == DLL_THREAD_ATTACH)
if (!_CRT_INIT(hinstDLL, fdwReason, lpReserved))
return(FALSE);
if (fdwReason == DLL_PROCESS_DETACH || fdwReason == DLL_THREAD_DETACH)
if (!_CRT_INIT(hinstDLL, fdwReason, lpReserved))
return(FALSE);
return(TRUE);
}
Remarque
Cela n’est pas nécessaire si vous utilisez DllMain() et -entry:_DllMainCRTStartup@12.
Initialiser des DLL MFC classiques
Étant donné que les DLL MFC standard ont un CWinApp objet, elles doivent effectuer leurs tâches d'initialisation et de terminaison au même emplacement qu'une application MFC : dans les fonctions membres InitInstance et ExitInstance de la classe dérivée CWinApp de la DLL. Étant donné que MFC fournit une DllMain fonction appelée par _DllMainCRTStartup, DLL_PROCESS_ATTACH et DLL_PROCESS_DETACH, vous ne devriez pas écrire votre propre fonction DllMain. La fonction DllMain fournie par MFC appelle InitInstance lorsque votre DLL est chargée et appelle ExitInstance avant que la DLL ne soit déchargée.
Une DLL MFC standard peut effectuer le suivi de plusieurs threads en appelant TlsAlloc et TlsGetValue dans sa InitInstance fonction. Ces fonctions permettent à la DLL de suivre les données spécifiques au thread.
Dans votre DLL MFC standard qui lie dynamiquement à MFC, si vous utilisez respectivement OLE MFC, base de données MFC (ou DAO), ou sockets MFC, les DLL d'extension MFC de débogage MFCOversionD.dll, MFCDversionD.dll, et MFCNversionD.dll (où version est le numéro de version) sont liées automatiquement. Vous devez appeler l’une des fonctions d’initialisation CWinApp::InitInstanceprédéfinies suivantes pour chacune de ces DLL que vous utilisez dans la DLL MFC standard.
| Type de prise en charge de MFC | Fonction d’initialisation à appeler |
|---|---|
| OLE MFC (MFCOversionD.dll) | AfxOleInitModule |
| Base de données MFC (MFCDversionD.dll) | AfxDbInitModule |
| Sockets MFC (MFCNversionD.dll) | AfxNetInitModule |
Initialisez les DLL d’extension MFC
Étant donné que les DLL d’extension MFC n’ont pas d’objet CWinAppdérivé (comme les DLL MFC standard), vous devez ajouter votre code d’initialisation et de terminaison à la DllMain fonction générée par l’Assistant DLL MFC.
L'Assistant fournit le code suivant pour les DLL d'extension MFC. Dans le code, PROJNAME il s’agit d’un espace réservé pour le nom de votre projet.
#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
}
La création d’un CDynLinkLibrary objet pendant l’initialisation permet à la DLL d’extension MFC d’exporter des CRuntimeClass objets ou des ressources vers l’application cliente.
Si vous allez utiliser votre DLL d’extension MFC à partir d’une ou plusieurs DLL MFC standard, vous devez exporter une fonction d’initialisation qui crée un CDynLinkLibrary objet. Cette fonction doit être appelée à partir de chacune des DLL MFC standard qui utilisent la DLL d’extension MFC. Un emplacement approprié pour appeler cette fonction d’initialisation se trouve dans la InitInstance fonction membre de l’objet dérivé de CWinAppla DLL MFC standard avant d’utiliser l’une des classes ou fonctions exportées de la DLL d’extension MFC.
Dans le DllMain que l'Assistant DLL MFC génère, l'appel à AfxInitExtensionModule capture les classes runtime du module (structures CRuntimeClass) et ses usines d'objets (objets COleObjectFactory) pour être utilisées lors de la création de l'objet CDynLinkLibrary. Vous devez vérifier la valeur de retour de AfxInitExtensionModule; si une valeur zéro est retournée par AfxInitExtensionModule, retournez zéro de votre DllMain fonction.
Si votre DLL d'extension MFC est explicitement liée à un exécutable (ce qui signifie que l'exécutable appelle AfxFreeLibrary appel). Si votre DLL d’extension MFC est liée implicitement à l’application, l’appel à AfxTermExtensionModule n’est pas nécessaire.
Les applications qui établissent explicitement un lien avec les DLL d'extension MFC doivent appeler AfxTermExtensionModule lors de la libération de la DLL. Ils doivent également utiliser AfxLoadLibrary et AfxFreeLibrary (au lieu des fonctions LoadLibrary Win32 et FreeLibrary) si l’application utilise plusieurs threads. L’utilisation de AfxLoadLibrary et AfxFreeLibrary garantit que le code de démarrage et d’arrêt qui s’exécute lorsque la DLL d’extension MFC est chargée et déchargée n’altère pas l’état MFC global.
Étant donné que le fichier MFCx0.dll est entièrement initialisé lorsqu'il est appelé via DllMain, vous pouvez allouer de la mémoire et appeler des fonctions MFC au sein de DllMain, contrairement à la version 16 bits de MFC.
Les DLL d’extension peuvent prendre en charge le multithreading en gérant les DLL_THREAD_ATTACH et DLL_THREAD_DETACH cas dans la fonction DllMain. Ces cas sont passés à DllMain lorsque les threads s'attachent et se détachent de la DLL. L’appel TlsAlloc lorsqu’une DLL est attachée permet à la DLL de conserver les index TLS (Thread Local Storage) pour chaque thread attaché à la DLL.
Le fichier d’en-tête Afxdllx.h contient des définitions spéciales pour les structures utilisées dans les DLL d’extension MFC, telles que la définition pour AFX_EXTENSION_MODULE et CDynLinkLibrary. Vous devez inclure ce fichier d’en-tête dans votre DLL d’extension MFC.
Remarque
Il est important que vous ne définissiez ni ne dédefiniez aucune des _AFX_NO_XXX macros dans (pch.hdans Visual Studio 2017 et versions antérieures stdafx.h ). Ces macros existent uniquement pour vérifier si une plateforme cible particulière prend en charge cette fonctionnalité ou non. Vous pouvez écrire votre programme pour vérifier ces macros (par exemple), #ifndef _AFX_NO_OLE_SUPPORTmais votre programme ne doit jamais définir ou annuler la définition de ces macros.
Un exemple de fonction d’initialisation qui gère la multithreading est inclus dans l’utilisation du stockage local thread dans une bibliothèque de liens dynamiques dans le Kit de développement logiciel (SDK) Windows. Notez que l’exemple contient une fonction de point d’entrée appelée LibMain, mais vous devez nommer cette fonction DllMain afin qu’elle fonctionne avec les bibliothèques de runtime MFC et C.
Voir aussi
Création de DLL C/C++ dans Visual Studio
Point d’entrée DllMain
Meilleures pratiques en matière de bibliothèque de liens dynamiques