Point d’entrée DllMain

Point d’entrée facultatif dans une bibliothèque de liens dynamiques (DLL). Lorsque le système démarre ou termine un processus ou un thread, il appelle la fonction de point d’entrée pour chaque DLL chargée à l’aide du premier thread du processus. Le système appelle également la fonction de point d’entrée pour une DLL lorsqu’elle est chargée ou déchargée à l’aide des fonctions LoadLibrary et FreeLibrary .

Exemple

BOOL WINAPI DllMain(
    HINSTANCE hinstDLL,  // handle to DLL module
    DWORD fdwReason,     // reason for calling function
    LPVOID lpvReserved )  // reserved
{
    // Perform actions based on the reason for calling.
    switch( fdwReason ) 
    { 
        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:
        
            if (lpvReserved != nullptr)
            {
                break; // do not do cleanup if process termination scenario
            }
            
         // Perform any necessary cleanup.
            break;
    }
    return TRUE;  // Successful DLL_PROCESS_ATTACH.
}

Il s’agit d’un exemple de la fonction Dynamic-Link Library Entry-Point.

Avertissement

Il existe des limites importantes quant à ce que vous pouvez faire en toute sécurité dans un point d’entrée DLL. Consultez Bonnes pratiques générales pour les API Windows spécifiques qui ne peuvent pas être appelées dans DllMain. Si vous avez besoin de quelque chose d’autre que l’initialisation la plus simple, faites-le dans une fonction d’initialisation pour la DLL. Vous pouvez exiger des applications qu’elles appellent la fonction d’initialisation après l’exécution de DllMain et avant d’appeler d’autres fonctions dans la DLL.

Syntaxe

BOOL WINAPI DllMain(
  _In_ HINSTANCE hinstDLL,
  _In_ DWORD     fdwReason,
  _In_ LPVOID    lpvReserved
);

Paramètres

hinstDLL [in]

Handle du module DLL. La valeur est l’adresse de base de la DLL. L’HINSTANCE d’une DLL est identique à la HMODULE de la DLL, de sorte que hinstDLL peut être utilisé dans les appels à des fonctions qui nécessitent un handle de module.

fdwReason [in]

Code de raison qui indique pourquoi la fonction de point d’entrée DLL est appelée. Ce paramètre peut prendre les valeurs suivantes.

Valeur Signification
DLL_PROCESS_ATTACH
1
La DLL est chargée dans l’espace d’adressage virtuel du processus en cours à la suite du démarrage du processus ou d’un appel à LoadLibrary. Les DLL peuvent utiliser cette possibilité pour initialiser des données instance ou pour utiliser la fonction TlsAlloc pour allouer un index de stockage local de thread (TLS).
Le paramètre lpvReserved indique si la DLL est chargée de manière statique ou dynamique.
DLL_PROCESS_DETACH
0
La DLL est déchargée de l’espace d’adressage virtuel du processus appelant, car elle a été chargée sans succès ou le nombre de références a atteint zéro (les processus se sont arrêtés ou ont appelé FreeLibrary une fois pour chaque fois qu’il a appelé LoadLibrary).
Le paramètre lpvReserved indique si la DLL est déchargée à la suite d’un appel FreeLibrary , d’un échec de chargement ou de l’arrêt du processus.
La DLL peut utiliser cette opportunité pour appeler la fonction TlsFree pour libérer tous les index TLS alloués à l’aide de TlsAlloc et pour libérer les données locales des threads.
Notez que le thread qui reçoit la notification DLL_PROCESS_DETACH n’est pas nécessairement le même que celui qui a reçu la notification DLL_PROCESS_ATTACH .
DLL_THREAD_ATTACH
2
Le processus actuel crée un thread. Dans ce cas, le système appelle la fonction de point d’entrée de toutes les DLL actuellement attachées au processus. L’appel est effectué dans le contexte du nouveau thread. Les DLL peuvent en profiter pour initialiser un emplacement TLS pour le thread. Un thread appelant la fonction de point d’entrée DLL avec DLL_PROCESS_ATTACH n’appelle pas la fonction de point d’entrée DLL avec DLL_THREAD_ATTACH.
Notez que la fonction de point d’entrée d’une DLL est appelée avec cette valeur uniquement par les threads créés après le chargement de la DLL par le processus. Lorsqu’une DLL est chargée à l’aide de LoadLibrary, les threads existants n’appellent pas la fonction de point d’entrée de la DLL nouvellement chargée.
DLL_THREAD_DETACH
3
Un thread sort proprement. Si la DLL a stocké un pointeur vers la mémoire allouée dans un emplacement TLS, elle doit en profiter pour libérer la mémoire. Le système appelle la fonction de point d’entrée de toutes les DLL actuellement chargées avec cette valeur. L’appel est effectué dans le contexte du thread de sortie.

lpvReserved [in]

Si fdwReason est DLL_PROCESS_ATTACH, lpvReserved a la valeur NULL pour les charges dynamiques et non NULL pour les charges statiques.

Si fdwReason est DLL_PROCESS_DETACH, lpvReserved a la valeur NULL si FreeLibrary a été appelé ou si le chargement de dll a échoué et non NULL si le processus se termine.

Valeur retournée

Lorsque le système appelle la fonction DllMain avec la valeur DLL_PROCESS_ATTACH , la fonction retourne TRUE si elle réussit ou FALSE si l’initialisation échoue. Si la valeur de retour est FALSE lorsque DllMain est appelé parce que le processus utilise la fonction LoadLibrary , LoadLibrary retourne NULL. (Le système appelle immédiatement votre fonction de point d’entrée avec DLL_PROCESS_DETACH et décharge la DLL.) Si la valeur de retour est FALSE lorsque DllMain est appelé lors de l’initialisation du processus, le processus se termine par une erreur. Pour obtenir des informations détaillées sur l’erreur, appelez GetLastError.

Lorsque le système appelle la fonction DllMain avec une valeur autre que DLL_PROCESS_ATTACH, la valeur de retour est ignorée.

Notes

DllMain est un espace réservé pour le nom de fonction défini par la bibliothèque. Vous devez spécifier le nom réel que vous utilisez lorsque vous générez votre DLL. Pour plus d’informations, consultez la documentation fournie avec vos outils de développement.

Au démarrage initial du processus ou après un appel à LoadLibrary, le système analyse la liste des DLL chargées pour le processus. Pour chaque DLL qui n’a pas encore été appelée avec la valeur DLL_PROCESS_ATTACH , le système appelle la fonction de point d’entrée de la DLL. Cet appel est effectué dans le contexte du thread qui a entraîné la modification de l’espace d’adressage du processus, comme le thread principal du processus ou le thread qui a appelé LoadLibrary. L’accès au point d’entrée est sérialisé par le système à l’échelle du processus. Les threads dans DllMain contiennent le verrou du chargeur afin qu’aucune DLL supplémentaire ne puisse être chargée ou initialisée dynamiquement.

Si la fonction de point d’entrée de la DLL retourne FALSE à la suite d’une notification DLL_PROCESS_ATTACH , elle reçoit une notification DLL_PROCESS_DETACH et la DLL est déchargée immédiatement. Toutefois, si le code DLL_PROCESS_ATTACH lève une exception, la fonction de point d’entrée ne reçoit pas la notification DLL_PROCESS_DETACH .

Il existe des cas dans lesquels la fonction de point d’entrée est appelée pour un thread de fin, même si la fonction de point d’entrée n’a jamais été appelée avec DLL_THREAD_ATTACH pour le thread :

  • Le thread étant le thread initial du processus, le système a appelé la fonction de point d’entrée avec la valeur DLL_PROCESS_ATTACH .
  • Le thread était déjà en cours d’exécution lorsqu’un appel à la fonction LoadLibrary a été effectué, de sorte que le système n’a jamais appelé la fonction de point d’entrée pour celle-ci.

Lorsqu’une DLL est déchargée d’un processus à la suite d’un échec de chargement de la DLL, de l’arrêt du processus ou d’un appel à FreeLibrary, le système n’appelle pas la fonction de point d’entrée de la DLL avec la valeur DLL_THREAD_DETACH pour les threads individuels du processus. La DLL reçoit uniquement une notification DLL_PROCESS_DETACH . Les DLL peuvent en profiter pour propre toutes les ressources pour tous les threads connus de la DLL.

Lors de la gestion des DLL_PROCESS_DETACH, une DLL doit libérer des ressources telles que la mémoire du tas uniquement si la DLL est déchargée dynamiquement (le paramètre lpvReserved est NULL). Si le processus se termine (le paramètre lpvReserved n’est pas NULL), tous les threads du processus, à l’exception du thread actuel, ont déjà quitté ou ont été explicitement arrêtés par un appel à la fonction ExitProcess , ce qui peut laisser certaines ressources de processus, telles que des tas, dans un état incohérent. Dans ce cas, il n’est pas sûr que la DLL propre les ressources. Au lieu de cela, la DLL doit permettre au système d’exploitation de récupérer la mémoire.

Si vous terminez un processus en appelant TerminateProcess ou TerminateJobObject, les DLL de ce processus ne reçoivent pas de notifications DLL_PROCESS_DETACH . Si vous terminez un thread en appelant TerminateThread, les DLL de ce thread ne reçoivent pas de notifications DLL_THREAD_DETACH .

La fonction de point d’entrée ne doit effectuer que des tâches d’initialisation ou d’arrêt simples. Il ne doit pas appeler la fonction LoadLibrary ou LoadLibraryEx (ou une fonction qui appelle ces fonctions), car cela peut créer des boucles de dépendance dans l’ordre de chargement de dll. Cela peut entraîner l’utilisation d’une DLL avant que le système n’ait exécuté son code d’initialisation. De même, la fonction de point d’entrée ne doit pas appeler la fonction FreeLibrary (ou une fonction qui appelle FreeLibrary) pendant l’arrêt du processus, car cela peut entraîner l’utilisation d’une DLL une fois que le système a exécuté son code d’arrêt.

Étant donné que Kernel32.dll est chargé dans l’espace d’adressage du processus lorsque la fonction de point d’entrée est appelée, l’appel de fonctions dans Kernel32.dll n’entraîne pas l’utilisation de la DLL avant l’exécution de son code d’initialisation. Par conséquent, la fonction de point d’entrée peut appeler des fonctions dans Kernel32.dll qui ne chargent pas d’autres DLL. Par exemple, DllMain peut créer des objets de synchronisation tels que des sections critiques et des mutex, et utiliser TLS. Malheureusement, il n’existe pas de liste complète des fonctions sécurisées dans Kernel32.dll.

Les fonctions appelantes qui nécessitent des DLL autres que Kernel32.dll peuvent entraîner des problèmes difficiles à diagnostiquer. Par exemple, l’appel des fonctions Utilisateur, Shell et COM peut entraîner des erreurs de violation d’accès, car certaines fonctions chargent d’autres composants système. À l’inverse, l’appel de fonctions telles que celles-ci pendant l’arrêt peut entraîner des erreurs de violation d’accès, car le composant correspondant a peut-être déjà été déchargé ou non initialisé.

Étant donné que les notifications DLL sont sérialisées, les fonctions de point d’entrée ne doivent pas tenter de communiquer avec d’autres threads ou processus. Des interblocages peuvent se produire en conséquence.

Pour plus d’informations sur les meilleures pratiques lors de l’écriture d’une DLL, consultez Meilleures pratiques en matière de bibliothèque de liens dynamiques.

Si votre DLL est liée à la bibliothèque d’exécution C (CRT), le point d’entrée fourni par le CRT appelle les constructeurs et les destructeurs pour les objets C++ globaux et statiques. Par conséquent, ces restrictions pour DllMain s’appliquent également aux constructeurs et aux destructeurs et à tout code appelé à partir d’eux.

Envisagez d’appeler DisableThreadLibraryCalls lors de la réception de DLL_PROCESS_ATTACH, sauf si votre DLL est liée à une bibliothèque d’exécution C statique (CRT).

Spécifications

Condition requise Valeur
Client minimal pris en charge
Windows XP [applications de bureau uniquement]
Serveur minimal pris en charge
Windows Server 2003 [applications de bureau uniquement]
En-tête
Process.h

Voir aussi

Dynamic-Link Library Entry-Point Function

Fonctions de bibliothèque de liens dynamiques

FreeLibrary

GetModuleFileName

LoadLibrary

TlsAlloc

TlsFree

DisableThreadLibraryCalls