通过在DLL边界的潜在错误CRT对象

当通过 C 文件 (CRT)对象 (如文件句柄、区域设置和环境变量设置为或在 DLL 外部时 (在 DLL 边界函数调用),可能发生意外的行为,如果 DLL,以及调入 DLL 的文件,使用 CRT 库的不同副本。

一个相关,则会出现问题。您分配内存 (显式使用 new 或 malloc或隐式使用 strdupstrstreambuf::str,等等) 后通过在将释放的 DLL 边界的指针。 ,如果 DLL 及其用户使用 CRT 库,不同的副本这可能导致内存存取冲突或堆损坏。

在调试期间例如,此问题的另一种症状将在输出窗口中的错误:

堆 []:无效地址指定要 RtlValidateHeap (#, #)

原因

CRT 库的每个副本都有一个个不同的状态。 因此, CRT 对象 (如文件句柄,环境变量和区域设置用于分发或设置这些对象 CRT 的副本才有效。 当 DLL 及其用户使用 CRT 库中的不同副本,则在另一端无法通过在 DLL 边界的这些 CRT 对象和按预期方式正确会选取。

此外,,因为 CRT 库的每个副本都有自己的堆管理器,分配中存在一个 CRT 库中并传入 CRT 库的多个不同的副本会释放的 DLL 边界的指针是堆损坏的一个可能的原因。

如果您设计的 DLL,通过跨边界的 CRT 对象或分配内存并希望它在 DLL 外被释放,您限制 DLL 用户使用 CRT 库的同一副本与 DLL。 ,只有当两个与 CRT DLL,同一版本的链接到 DLL 及其用户使用 CRT 库的同一副本。 这可能是问题,如果您使用 DLL 的 Visual C++ 5.0 混合由 Visual C++ 4.1 生成或更早版本的应用程序编译。 由于 Visual C++ 4.1 使用的 CRT 库的 DLL 版本是 msvcrt40.dll,和可视使用的一个 5.0 是 msvcrt.dll,不能生成应用程序使用 CRT 库的同一副本。这些 DLL。

但是,也存在例外情况。 在美国英语版本和 Windows 2000 的其他一些本地化版本,例如德语、法语和捷克,该 msvcrt40.dll (4.20 版) 的代运方版本中提供。 结果,因此,即使 DLL 与 msvcrt40.dll 链接,并且其用户与 msvcrt.dll 链接,则仍使用 CRT 库的同一副本,因为所有调用对 msvcrt40.dll 被转发到 msvcrt.dll。

但是 msvcrt40.dll 的此代运方版本不 Windows 2000 的某些本地化版本,如日语和中文、朝鲜语。 因此,因此,如果应用程序以这些操作系统,则需要用于获取不依赖于 msvcrt40.dll DLL 的已升级的版本或修改应用程序不依赖于使用 CRT 库的同一副本。 如果您开发的 DLL,这意味着重新生成它与 Visual C++ 4.2 或更高版本。 如果是第三方 DLL,需要与升级的供应商联系。

请注意 msvcrt40.dll (4.20 版) 的此代运方 DLL 版本无法重新发布。

示例

ms235460.collapse_all(zh-cn,VS.110).gif说明

此示例通过在 DLL 边界的文件句柄。

DLL 和 .exe 文件是用 /MD 编译,它们共享 CRT 的一个副本。

如果重新生成使用 /MT,以便使用 CRT 的单独副本,运行结果 test1Main.exe 导致访问冲突。

ms235460.collapse_all(zh-cn,VS.110).gif代码

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

ms235460.collapse_all(zh-cn,VS.110).gif代码

// test1Main.cpp
// compile with: /MD 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" );
}

ms235460.collapse_all(zh-cn,VS.110).gifOutput

this is a string

示例

ms235460.collapse_all(zh-cn,VS.110).gif说明

此示例通过在 DLL 边界的环境变量。

ms235460.collapse_all(zh-cn,VS.110).gif代码

// test2Dll.cpp
// compile with: /MT /LD
#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 );
}

ms235460.collapse_all(zh-cn,VS.110).gif代码

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

void readEnv();

int main( void )
{
   _putenv( "MYLIB=c:\\mylib;c:\\yourlib" );
   readEnv();
}

ms235460.collapse_all(zh-cn,VS.110).gifOutput

MYLIB has not been set.

如果 DLL 和 .exe 文件是用 /MD 编译,以便仅使用 CRT 的一个副本,程序平稳运行并产生以下输出:

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

请参见

参考

CRT库功能