zoukankan      html  css  js  c++  java
  • 跨越DLL边界传递CRT对象潜在的错误

    跨越DLL边界传递CRT对象潜在的错误

     

    翻译:magictong(童磊)2013年5月

    版权:microsoft 

    原文地址:http://msdn.microsoft.com/en-us/library/ms235460(v=vs.80).aspx

     

     

     

    简介

    当你把C运行时(CRT)对象(譬如文件句柄、语言环境和环境变量等等)传入传出DLL时(通过调用DLL里面暴露的一些函数),如果这个DLL加载了一份(与可执行文件)不同的CRT库,可能发现意向不到的事情。

    有一个大家可能遇到过,相似的问题是,如果可执行程序在外面分配一块内存(显示的通过new或者malloc分配,或者通过调用strdup,strstreambuf::str等等函数隐式的分配),然后把分配得到的内存指针通过调用DLL暴露的函数传入DLL里面去释放,如果这个DLL加载了一份不同的CRT库,可能造成内存访问违例(AV)或者堆破坏。

    如果你正在调试程序,这个问题可能在调试输出窗口打印一条如下的错误信息:

    HEAP[]: Invalid Address specified to RtlValidateHeap(#,#)

     

     

    原因

    每个CRT库的副本都有一个单独并且独特的状态,因此,一些类似文件句柄,语言环境和环境变量这样的CRT对象,仅仅在分配它或者设置它的CRT库里面有效。当一个DLL和使用这个DLL的程序分别使用了两个不同的CRT库时,如果你把这些CRT对象在DLL和使用DLL的程序两边相互传递,并希望程序运行正常,那是不太可能的。

    另外,因为每个CRT库的副本都有一个自己的堆管理器,在一个CRT库里面分配的内存传递(通过调用DLL暴露的函数)到另外一个CRT库里面去释放可能导致堆破坏

    如果你想设计一个可以传递CRT对象的DLL,或者明确的在DLL内部分配内存,而在DLL的外部释放,那么你必须限制这个DLL的使用者使用和DLL一样的CRT库。而要想DLL的使用者使用和DLL一样的CRT库除非二者链接到相同版本的CRT动态链接库。如果应用程序和DLL的编译环境不一样,譬如应用程序使用Visual C++5.0编译而DLL使用Visual C++ 4.1编译或者更早版本,因为Visual C++ 4.1使用的CRT库是msvcrt40.dll,而Visual C++5.0使用的是msvcrt.dll,这可能会非常麻烦。你可能无法重新编译你的应用程序使得和DLL使用的CRT库一模一样。

    不过,有一个特殊情况。在一些特殊版本的Windows NT 4.0和Windows 2000里面(譬如,美国英语版本,德国,法国和捷克的本地化版本),使用了一个代理模式的msvcrt40.dll(具体版本是4.20)。在这些环境里面,虽然DLL链接的是msvcrt40.dll,DLL的使用者(应用程序)链接的是msvcrt.dll,但是因为所有对msvcrt40.dll的调用都被转调用到了msvcrt.dll,因此这种情况二者仍然使用的是同一个CRT库。

    然而,这个代理版本的msvcrt40.dll在Windows 95,Windows 98,Windows Me和其它一些本地化的Windows NT 4.0和Windows 2000(譬如日本,韩国和中国)版本里面是无效的。因此,如果你的应用程序的目标操作系统是这些环境,你需要获得一个不依赖msvcrt40.dll的升级版本的DLL,或者更改你的应用程序。如果你已经把这个DLL开发出来了,那么使用Visual C++ 4.2或者更高版本重新编译一下DLL,如果这个DLL是第三方提供的组件,你可能需要相关开发商提供一个升级。

     

     

    例子一

    描述

    这里是一个跨越DLL边界传递文件句柄的实例。

    假如DLL文件和.exe文件都是使用/MD来编译,那么它们使用同一份CRT库,这没有问题。

    如果你使用/MT来重新编译,使得它们使用不同的CRT库,然后运行test1Main.exe,马上就会出现访问违例(access violation)。

     

    代码

    // test1Dll.cpp

    // compile with: /MD /LD

    #include <stdio.h>

    __declspec(dllexport) void writeFile(FILE *stream)

    {

       char   s[] = "this is a string ";

       fprintf( stream, "%s", s );

       fclose( stream );

    }

     

    代码

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

    }

     

    输出

    this is a string

     

     

     

    例子二

    描述

    这个例子说明跨DLL传递环境变量的问题。

     

    代码

    // 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 ", libvar);

       else

          printf( "MYLIB has not been set. ");

       free( libvar );

    }

     

    代码

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

    }

    如果DLL和.EXE文件都使用/MD来编译,那么二者使用同一份CRT库,此时输出是New MYLIB variable is: c:mylib;c:yourlib,而如果二者都使用/MT编译,那么输出将是MYLIB has not been set.

    (注:以上两个实例,译者magictong并没有进行过测试,如果有问题,请告诉magictong)

     

     

    参考文档

    C运行时库 http://msdn.microsoft.com/en-us/library/abx4dbyh(v=vs.100).aspx

    http://blog.csdn.net/magictong/article/details/8927049

  • 相关阅读:
    git(1)-git关联GitHub-windows-转载
    jenkins(4)-jenkins配置邮件通知
    jenkins(3)-linux下安装jenkins(yum install方式)
    【PAT甲级】1090 Highest Price in Supply Chain (25 分)(DFS)
    【PAT甲级】1087 All Roads Lead to Rome (30 分)(MAP【int,string】,邻接表,DFS,模拟,SPFA)
    【PAT甲级】1018 Public Bike Management (30 分)(DFS,SPFA)
    Educational Codeforces Round 61 (Rated for Div. 2) G(线段树,单调栈)
    Atcoder Grand Contest 032C(欧拉回路,DFS判环)
    Educational Codeforces Round 62 (Rated for Div. 2)E(染色DP,构造,思维,组合数学)
    Atcoder Grand Contest 031C(构造,思维,异或,DFS)
  • 原文地址:https://www.cnblogs.com/findumars/p/6143429.html
Copyright © 2011-2022 走看看