Potensi kesalahan melewati objek CRT di seluruh batas DLL

Kode Anda mungkin mengalami kesalahan saat Anda meneruskan objek C Runtime (CRT) seperti handel file, lokal, dan variabel lingkungan ke dalam atau keluar dari DLL. Panggilan fungsi di seluruh batas DLL dapat menyebabkan perilaku tak terduga jika DLL dan file apa pun yang dipanggil ke DLL menggunakan salinan pustaka CRT yang berbeda.

Masalah terkait dapat terjadi ketika Anda mengalokasikan memori (baik secara eksplisit dengan new atau malloc, atau secara implisit dengan strdup, , strstreambuf::strdan sebagainya) dan kemudian meneruskan penunjuk di seluruh batas DLL tempatnya dibeberkan. Pointer tersebut dapat menyebabkan pelanggaran akses memori, atau kerusakan timbunan, jika DLL dan konsumennya menggunakan salinan pustaka CRT yang berbeda.

Gejala lain dari masalah ini adalah kesalahan di jendela output selama penelusuran kesalahan seperti HEAP[]: Invalid Address specified to RtlValidateHeap(#,#)

Penyebab

Setiap salinan pustaka CRT memiliki status terpisah dan berbeda, disimpan dalam penyimpanan lokal utas oleh aplikasi atau DLL Anda.

Objek CRT seperti handel file, variabel lingkungan, dan lokal hanya valid untuk salinan CRT di aplikasi atau DLL tempat objek ini dialokasikan atau diatur. Ketika DLL dan kliennya menggunakan salinan pustaka CRT yang berbeda, Anda tidak dapat mengharapkan objek CRT ini digunakan dengan benar saat diteruskan di seluruh batas DLL.

Ini terutama berlaku untuk versi CRT sebelum Universal CRT di Visual Studio 2015 dan yang lebih baru. Ada pustaka CRT khusus versi untuk setiap versi Visual Studio yang dibuat dengan Visual Studio 2013 atau yang lebih lama. Detail implementasi internal CRT, seperti struktur data dan konvensi penamaan, berbeda di setiap versi. Menautkan kode secara dinamis yang dikompilasi untuk satu versi CRT ke versi CRT DLL yang berbeda belum pernah didukung. Kadang-kadang itu akan berhasil, tetapi karena keberuntungan daripada desain.

Setiap salinan pustaka CRT memiliki manajer timbunannya sendiri. Ini dapat menyebabkan kerusakan tumpukan jika Anda mengalokasikan memori dalam satu pustaka CRT dan meneruskan penunjuk di seluruh batas DLL untuk dibebaskan oleh salinan pustaka CRT yang berbeda. Jika DLL Anda melewati objek CRT di seluruh batas DLL, atau mengalokasikan memori yang dibebaskan di luar DLL, klien DLL harus menggunakan salinan pustaka CRT yang sama dengan DLL.

DLL dan kliennya biasanya menggunakan salinan pustaka CRT yang sama hanya jika keduanya ditautkan pada waktu pemuatan ke versi DLL CRT yang sama. Karena versi DLL dari pustaka Universal CRT yang digunakan oleh Visual Studio 2015 dan yang lebih baru sekarang merupakan komponen Windows yang disebarkan secara terpusat (ucrtbase.dll), itu sama untuk aplikasi yang dibangun dengan Visual Studio 2015 dan versi yang lebih baru. Namun, bahkan ketika kode CRT identik, Anda tidak dapat memberikan memori yang dialokasikan dalam satu timbunan ke komponen yang menggunakan timbunan yang berbeda.

Contoh: Meneruskan handel file di seluruh batas DLL

Deskripsi

Contoh ini meneruskan handel file di seluruh batas DLL.

File DLL dan .exe dibangun dengan /MD, sehingga mereka berbagi satu salinan CRT.

Jika Anda membangun kembali sehingga /MT mereka menggunakan salinan terpisah CRT, menjalankan hasil yang dihasilkan test1Main.exe dalam pelanggaran akses.

File test1Dll.cppsumber 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 );
}

File test1Main.cppsumber yang dapat dieksekusi :

// 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

Contoh: Meneruskan variabel lingkungan di seluruh batas DLL

Deskripsi

Contoh ini meneruskan variabel lingkungan di seluruh batas DLL.

File test2Dll.cppsumber 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 );
}

File test2Main.cppsumber yang dapat dieksekusi :

// 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.

Jika Anda membangun file DLL dan EXE dengan menggunakan /MD, sehingga hanya satu salinan CRT yang digunakan, program berjalan dengan sukses dan menghasilkan output berikut:

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

Baca juga

File runtime C (CRT) dan C++ Standard Library (STL) .lib