Partager via


Utiliser l’exécution C

Cet article explique comment utiliser l’exécution C.

Version du produit d’origine : Visual C++
Numéro de la base de connaissances d’origine : 94248

Section 1 : Trois formes de bibliothèques runtime C (CRT) sont disponibles

Il existe trois formes de la bibliothèque runtime C fournie avec le Kit de développement logiciel (SDK) Win32 :

  • LIBC. LIB est une bibliothèque liée statiquement pour les programmes à thread unique.

  • LIBCMT. LIB est une bibliothèque liée statiquement qui prend en charge les programmes multithread.

  • CRTDLL. LIB est une bibliothèque d’importation pour CRTDLL.DLL qui prend également en charge les programmes multithreads. CRTDLL.DLL elle-même fait partie de Windows NT.

Microsoft Visual C++ édition 32 bits contient également ces trois formulaires. Toutefois, le CRT dans une DLL est nommé MSVCRT. LIB. La DLL est redistribuable. Son nom dépend de la version de VC++ (autrement dit, MSVCRT10.DLL ou MSVCRT20.DLL). Notez toutefois que MSVCRT10.DLL n’est pas pris en charge sur Win32s, tandis que CRTDLL. LIB est pris en charge sur Win32s. MSVCRT20.DLL est disponible dans deux versions : une pour Windows NT et l’autre pour Win32s.

Section 2 : Utilisation des bibliothèques CRT lors de la génération d’une DLL

Lors de la génération d’une DLL qui utilise l’une des bibliothèques runtime C, afin de vous assurer que le CRT est correctement initialisé, soit

  1. La fonction d’initialisation doit être nommée DllMain() et le point d’entrée doit être spécifié avec l’option éditeur de liens -entry:_DllMainCRTStartup@12

    ou

  2. Le point d’entrée de la DLL doit appeler CRT_INIT() explicitement l’attachement de processus et le détachement du processus.

Cela permet aux bibliothèques d’exécution C d’allouer et d’initialiser correctement les données d’exécution C lorsqu’un processus ou un thread est attaché à la DLL, de nettoyer correctement les données d’exécution 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. Utilisez-les comme exemple. Reportez-vous également à la documentation de référence du programmeur Win32 et DllEntryPoint() à la documentation Visual C++ pour DllMain(). Notez que les DllMainCRTStartup() appels CRT_INIT() et CRT_INIT() appelleront dllMain() de votre application, s’il existe.

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 :

  1. S’il n’existe aucune fonction d’entrée qui exécute le code d’initialisation, spécifiez CRT_INIT() comme point d’entrée de la DLL. En supposant que vous avez inclus NTWIN32. MAK, qui définit DLLENTRY @ 12, ajoutez l’option à la ligne de lien de la DLL :-entry:_CRT_INIT$(DLLENTRY).

    ou

  2. Si vous disposez de votre propre point d’entrée DLL, procédez comme suit dans le point d’entrée :

    1. Utilisez ce prototype 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 DllEntryPoint ; les mêmes valeurs sont retournées.

    2. Activé DLL_PROCESS_ATTACH et DLL_THREAD_ATTACH (voir DllEntryPoint dans la référence de l’API Win32 pour plus d’informations sur ces indicateurs), appelez CRT_INIT()d’abord, avant que toutes les fonctions runtime C soient appelées ou toutes les opérations à virgule flottante sont effectuées.

    3. Appelez votre propre code d’initialisation/de thread/arrêt.

    4. EnfinDLL_PROCESS_DETACH, appelez CRT_INIT() en dernier, une fois que toutes les fonctions d’exécution C ont été appelées et que toutes les opérations à virgule flottante sont terminéesDLL_THREAD_DETACH.

Veillez à CRT_INIT() transmettre tous les paramètres du point d’entrée ; CRT_INIT() attend que ces paramètres ne fonctionnent pas de manière fiable s’ils sont omis (en particulier, fdwReason est nécessaire pour déterminer si l’initialisation ou l’arrêt du processus est nécessaire).

Voici un exemple de fonction de point d’entrée squelette qui montre quand et comment effectuer ces appels CRT_INIT() dans le point d’entrée 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);
}

Note

Cela n’est pas nécessaire si vous utilisez DllMain() et -entry:_DllMainCRTStartup@12.

Section 3 : Utilisation de NTWIN32. MAK pour simplifier le processus de génération

Il existe des macros définies dans NTWIN32. MAK qui peut être utilisé pour simplifier vos makefiles et s’assurer qu’ils sont correctement générés pour éviter les conflits. Pour cette raison, Microsoft recommande vivement d’utiliser NTWIN32. MAK et les macros qui y sont contenues.

Pour la compilation, utilisez : $(cvarsdll) for apps/DLLs using CRT in a DLL.

Pour la liaison, utilisez l’une des options suivantes :

  • $(conlibsdll) for console apps/DLLs using CRT in a DLL
  • $(guilibsdll) for GUI apps using CRT in a DLL

Section 4 : Problèmes rencontrés lors de l’utilisation de plusieurs bibliothèques CRT

Si une application qui effectue des appels au moment de l’exécution C lie à une DLL qui effectue également des appels au moment de l’exécution C, sachez que s’ils sont tous les deux liés à l’une des bibliothèques runtime C liées statiquement (LIBC). LIB ou LIBCMT. LIB), les .EXE et DLL auront des copies distinctes de toutes les fonctions d’exécution C et de toutes les variables globales. Cela signifie que les données au moment de l’exécution C ne peuvent pas être partagées entre le .EXE et la DLL. Voici quelques-uns des problèmes qui peuvent se produire :

  • Passage de handles de flux mis en mémoire tampon du .EXE/DLL à l’autre module

  • Allocation de mémoire avec un appel au moment de l’exécution C dans le .EXE/DLL et réaffectation ou libération de la mémoire dans l’autre module

  • Vérification ou définition de la valeur de la variable errno globale dans la .EXE/DLL et s’attend à ce qu’elle soit identique dans l’autre module. Un problème lié est l’appel perror() dans le module opposé à partir duquel l’erreur d’exécution C s’est produite, car perror() utilise errno.

Pour éviter ces problèmes, liez à la fois la .EXE et la DLL avec CRTDLL. LIB ou MSVCRT. LIB, qui permet à la fois à la .EXE et à la DLL d’utiliser l’ensemble commun de fonctions et de données contenues dans CRT dans une DLL, et les données au moment de l’exécution C, telles que les handles de flux, peuvent ensuite être partagées par la .EXE et la DLL.

Section 5 : Mélange de types de bibliothèques

Vous pouvez lier votre DLL à CRTDLL. LIB/MSVCRT. LIB quel que soit votre .EXE lié si vous évitez de mélanger des structures de données CRT et de transmettre des handles de fichiers CRT ou des pointeurs CRT FILE* à d’autres modules.

Lorsque vous mélangez des types de bibliothèques, respectez les éléments suivants :

  • Les handles de fichiers CRT peuvent uniquement être utilisés par le module CRT qui les a créés.

  • Les pointeurs CRT FILE* peuvent uniquement être utilisés par le module CRT qui les a créés.

  • La mémoire allouée avec la fonction malloc() CRT peut uniquement être libérée ou réaffectée par le module CRT qui l’a allouée.

Pour illustrer cela, tenez compte de l’exemple suivant :

  • .EXE est lié à MSVCRT. LIB
  • LA DLL A est liée à LIBCMT. LIB
  • LA DLL B est liée à CRTDLL. LIB

Si le .EXE crée un handle de fichier CRT à l’aide _create() ou _open(), ce handle de fichier peut uniquement être transmis à _lseek(), _write()_read(), , _close(), etc. dans le fichier .EXE. Ne transmettez pas ce handle de fichier CRT à l’une ou l’autre DLL. Ne transmettez pas de handle de fichier CRT obtenu à partir d’une DLL vers l’autre DLL ou à l'.EXE.

Si LA DLL A alloue un bloc de mémoire avec malloc(), seule la DLL A peut appeler free(), _expand()ou realloc() pour fonctionner sur ce bloc. Vous ne pouvez pas appeler malloc() la DLL A et essayer de libérer ce bloc de la .EXE ou de la DLL B.

Note

Si les trois modules ont été liés à CRTDLL. LIB ou les trois ont été liés à MSVCRT. LIb, ces restrictions ne s’appliquent pas.

Lors de la liaison de DLL avec LIBC. LIB, sachez que s’il existe une possibilité qu’une telle DLL soit appelée par un programme multithread, la DLL ne prend pas en charge plusieurs threads s’exécutant dans la DLL en même temps, ce qui peut entraîner des problèmes majeurs. S’il existe une possibilité que la DLL soit appelée par des programmes multithreads, veillez à la lier à l’une des bibliothèques qui prennent en charge les programmes multithreads (LIBCMT). LIB, CRTDLL. LIB ou MSVCRT. LIB).