Erreurs potentielles de passage d’objets CRT entre des frontières de DLL

Votre code peut avoir des erreurs lorsque vous transmettez des objets C Runtime (CRT), tels que des handles de fichiers, des paramètres régionaux et des variables d’environnement dans ou hors d’une DLL. Les appels de fonction au-delà de la limite DLL peuvent provoquer un comportement inattendu si la DLL et tous les fichiers qui appellent la DLL utilisent différentes copies des bibliothèques CRT.

Un problème lié peut se produire lorsque vous allouez de la mémoire (explicitement avec new ou mallocimplicitement avec strdup, strstreambuf::stret ainsi de suite), puis passez un pointeur sur une limite DLL où il est libéré. Ces pointeurs peuvent provoquer une violation d’accès à la mémoire ou une altération du tas, si la DLL et ses consommateurs utilisent différentes copies des bibliothèques CRT.

Un autre symptôme de ce problème est une erreur dans la fenêtre de sortie pendant le débogage, par exemple HEAP[]: Invalid Address specified to RtlValidateHeap(#,#)

Causes

Chaque copie de la bibliothèque CRT a un état séparé et distinct, conservé dans le stockage local de thread par votre application ou dans une DLL.

Les objets CRT tels que les handles de fichiers, les variables d’environnement et les paramètres régionaux sont valides uniquement pour la copie du CRT dans l’application ou la DLL où ces objets ont été alloués ou définis. Lorsqu’une DLL et ses clients utilisent différentes copies de la bibliothèque CRT, vous ne pouvez pas vous attendre à ce que ces objets CRT soient utilisés correctement lorsqu’ils sont passés au-delà de la limite DE DLL.

Il est particulièrement vrai des versions CRT avant le CRT universel dans Visual Studio 2015 et versions ultérieures. Dans Visual Studio 2013 et antérieur, chaque version de Visual Studio avait sa propre bibliothèque CRT. Les détails de l’implémentation interne du CRT, tels que les structures de données et les conventions d’affectation de noms, étaient différents dans chaque version. La liaison dynamique du code compilé pour une version du CRT à une autre version de la DLL CRT n’a jamais été prise en charge. Parfois, il fonctionnerait, mais à cause de la chance plutôt que de la conception.

Chaque copie de la bibliothèque CRT possède son propre gestionnaire de tas. Cela peut entraîner une altération du tas si vous allouez de la mémoire dans une bibliothèque CRT et transmettez le pointeur sur une limite DLL pour être libéré par une autre copie de la bibliothèque CRT. Si votre DLL transmet des objets CRT à travers la limite DE DLL ou alloue de la mémoire libérée en dehors de la DLL, les clients de la DLL doivent utiliser la même copie de la bibliothèque CRT que la DLL.

La DLL et ses clients utilisent normalement la même copie de la bibliothèque CRT uniquement si, au moment du chargement, ils sont tous deux liés à la même version de la DLL CRT. Étant donné que la version DLL de la bibliothèque CRT universelle utilisée par Visual Studio 2015 et versions ultérieures est désormais un composant Windows déployé de manière centralisée (ucrtbase.dll), il est le même pour les applications créées avec Visual Studio 2015 et versions ultérieures. Toutefois, même lorsque le code CRT est identique, vous ne pouvez pas donner de mémoire allouée dans un tas à un composant qui utilise un tas différent.

Exemple : Passer un handle de fichier à travers la limite dll

Description

Cet exemple passe un handle de fichiers sur une limite DDL.

Les fichiers DLL et .exe sont générés avec /MD, de sorte qu’ils partagent une seule copie du CRT.

Si vous régénérez pour /MT qu’ils utilisent des copies distinctes du CRT, l’exécution des résultats entraîne test1Main.exe une violation d’accès.

Fichier test1Dll.cppsource DLL :

// test1Dll.cpp
// compile with: cl /EHsc /W4 /MD /LD test1Dll.cpp
#include <stdio.h>
__declspec(dllexport) void writeFile(FILE *stream)
{
   char   s[] = "this is a string\n";
   fprintf( stream, "%s", s );
   fclose( stream );
}

Fichier test1Main.cppsource exécutable :

// test1Main.cpp
// compile with: cl /EHsc /W4 /MD test1Main.cpp test1Dll.lib
#include <stdio.h>
#include <process.h>
void writeFile(FILE *stream);

int main(void)
{
   FILE  * stream;
   errno_t err = fopen_s( &stream, "fprintf.out", "w" );
   writeFile(stream);
   system( "type fprintf.out" );
}
this is a string

Exemple : Passer des variables d’environnement entre les limites de DLL

Description

Cet exemple passe des variables d'environnement sur une limite DDL.

Fichier test2Dll.cppsource DLL :

// test2Dll.cpp
// compile with: cl /EHsc /W4 /MT /LD test2Dll.cpp
#include <stdio.h>
#include <stdlib.h>

__declspec(dllexport) void readEnv()
{
   char *libvar;
   size_t libvarsize;

   /* Get the value of the MYLIB environment variable. */
   _dupenv_s( &libvar, &libvarsize, "MYLIB" );

   if( libvar != NULL )
      printf( "New MYLIB variable is: %s\n", libvar);
   else
      printf( "MYLIB has not been set.\n");
   free( libvar );
}

Fichier test2Main.cppsource exécutable :

// test2Main.cpp
// compile with: cl /EHsc /W4 /MT test2Main.cpp test2dll.lib
#include <stdlib.h>
#include <stdio.h>

void readEnv();

int main( void )
{
   _putenv( "MYLIB=c:\\mylib;c:\\yourlib" );
   readEnv();
}
MYLIB has not been set.

Si vous générez à la fois les fichiers DLL et EXE à l’aide /MDde , afin qu’une seule copie du CRT soit utilisée, le programme s’exécute correctement et produit la sortie suivante :

New MYLIB variable is: c:\mylib;c:\yourlib

Voir aussi

Fichiers C runtime (CRT) et bibliothèque standard C++ (STL) .lib