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 malloc
implicitement avec strdup
, strstreambuf::str
et 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.cpp
source 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.cpp
source 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.cpp
source 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.cpp
source 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 /MD
de , 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