CRT nesnelerini DLL sınırlarından geçirirken olası hatalar

Dosya tanıtıcıları, yerel ayarlar ve ortam değişkenleri gibi C Çalışma Zamanı (CRT) nesnelerini DLL'ye veya DLL'den dışarı geçirirken kodunuz hatalarla karşılaşabilir. DLL ve DLL'ye çağrıda bulunan tüm dosyalar CRT kitaplıklarının farklı kopyalarını kullanıyorsa, DLL sınırındaki işlev çağrıları beklenmeyen davranışlara neden olabilir.

Bellek ayırdığınızda (açıkça veya veya ile new ya da örtük olarak , strstreambuf::str, vb. ilestrdup) ve ardından serbest kaldığı DLL sınırı boyunca bir işaretçi geçirdiğinizde ilgili bir sorun mallocoluşabilir. DLL ve tüketicileri CRT kitaplıklarının farklı kopyalarını kullanıyorsa, bu tür işaretçiler bellek erişim ihlaline veya yığın bozulmasına neden olabilir.

Bu sorunun bir diğer belirtisi de hata ayıklama sırasında çıkış penceresindeki bir hatadır, örneğin HEAP[]: Invalid Address specified to RtlValidateHeap(#,#)

Nedenler

CRT kitaplığının her kopyası, uygulamanız veya DLL'niz tarafından iş parçacığı yerel depolama alanında tutulan ayrı ve ayrı bir duruma sahiptir.

Dosya tanıtıcıları, ortam değişkenleri ve yerel ayarlar gibi CRT nesneleri yalnızca bu nesnelerin ayrıldığı veya ayarlandığı uygulama veya DLL'deki CRT kopyası için geçerlidir. Dll ve istemcileri CRT kitaplığının farklı kopyalarını kullandığında, DLL sınırından geçirildiğinde bu CRT nesnelerinin doğru kullanılmasını bekleyemezsiniz.

Özellikle Visual Studio 2015 ve sonraki sürümlerde Evrensel CRT öncesi CRT sürümleri için geçerlidir. Visual Studio 2013 veya önceki sürümleriyle oluşturulmuş her Visual Studio sürümü için sürüme özgü bir CRT kitaplığı vardı. CRT'nin veri yapıları ve adlandırma kuralları gibi iç uygulama ayrıntıları her sürümde farklıydı. CRT'nin bir sürümü için derlenmiş kodu CRT DLL'nin farklı bir sürümüne dinamik olarak bağlama hiçbir zaman desteklenmedi. Bazen işe yarayacaktı ama tasarım yerine şans yüzünden.

CRT kitaplığının her kopyasının kendi yığın yöneticisi vardır. Bir CRT kitaplığında bellek ayırıp işaretçiyi bir DLL sınırına geçirerek CRT kitaplığının farklı bir kopyası tarafından serbest bırakıldıysa yığın bozulmasına neden olabilir. DLL'niz CRT nesnelerini DLL sınırından geçirirse veya DLL dışında boş bellek ayırırsa, DLL'nin istemcileri DLL ile aynı CRT kitaplığı kopyasını kullanmalıdır.

DLL ve istemcileri normalde CRT kitaplığının aynı kopyasını yalnızca her ikisi de yükleme zamanında CRT DLL'nin aynı sürümüne bağlıysa kullanır. Visual Studio 2015 ve üzeri tarafından kullanılan Evrensel CRT kitaplığının DLL sürümü artık merkezi olarak dağıtılan bir Windows bileşeni ()ucrtbase.dll olduğundan, Visual Studio 2015 ve sonraki sürümleriyle oluşturulan uygulamalar için de aynıdır. Ancak, CRT kodu aynı olsa bile, bir yığında ayrılan belleği farklı bir yığın kullanan bir bileşene veremezsiniz.

Örnek: DLL sınırı boyunca dosya tutamacını geçirme

Açıklama

Bu örnek, dll sınırı boyunca bir dosya tutamacını geçirir.

DLL ve .exe dosyaları ile /MDderlenir, böylece CRT'nin tek bir kopyasını paylaşırlar.

ile yeniden derleyerek /MT CRT'nin ayrı kopyalarını kullanmalarını sağlarsanız, sonuçta test1Main.exe elde edilen çalıştırma bir erişim ihlaline neden olur.

DLL kaynak dosyası test1Dll.cpp:

// 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 );
}

Yürütülebilir kaynak dosyası test1Main.cpp:

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

Örnek: Ortam değişkenlerini DLL sınırı boyunca geçirme

Açıklama

Bu örnek, ortam değişkenlerini DLL sınırı boyunca geçirir.

DLL kaynak dosyası test2Dll.cpp:

// 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 );
}

Yürütülebilir kaynak dosyası test2Main.cpp:

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

kullanarak hem DLL hem de EXE dosyalarını /MDoluşturursanız, CRT'nin yalnızca bir kopyası kullanılacak şekilde program başarıyla çalışır ve aşağıdaki çıkışı üretir:

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

Ayrıca bkz.

C çalışma zamanı (CRT) ve C++ Standart Kitaplığı (STL) .lib dosyaları