• 小心DLL链接静态库时的内存错误


    本文转自http://www.bennychen.cn/2010/09/%E5%B0%8F%E5%BF%83dll%E9%93%BE%E6%8E%A5%E9%9D%99%E6%80%81%E5%BA%93%E6%97%B6%E7%9A%84%E5%86%85%E5%AD%98%E9%94%99%E8%AF%AF/

    最近写的模块,在独立的应用程序中测试是没问题的,但把它装配成DLL后,再在另一个应用程序中调用时却出现了内存错误。程序的模块链接关系大概是这样的:

    module就是我所写的模块,在这里被封装为DLL,因为要使用json相关的功能,该DLL链接了一个静态库 (jsoncpp.lib)。最后在应用程序中导入并使用module.dll,同时因为在应用程序中也需要用到json,所以应用程序也链接了jsoncpp.lib。

    以下用一些伪代码来描述这些模块间的调用关系,以具现出这个错误。

    jsoncpp.lib为c++提供了功能齐全的json操作,其核心的类是Json::Value。(阅读本篇文章你无需了解太多json)

    module.dll中导出了一个接口:

    //ModuleClass.h
    #include "json/value.h"
     
    #if defined MODULE_EXPORTS
    #define MODULE_EXPORTS __declspec(dllexport)
    #else
    #define MODULE_EXPORTS __declspec(dllimport)
    #endif
     
    class ModuleClass
    {
    public:
        MODULE_EXPORTS void AllocSomeMemory( Json::Value &root )
        {
            // 这将申请一些内存,因为会new出一个Json::Value,并append到root上
            root.append( "testString" );
        }
    };

    应用程序:

    #include "json/value.h"
    #include "ModuleClass.h"
    int main()
    {
        Json::Value root;
        ModuleClass::AllocSomeMemory( root );
    }

    在Debug模式下,当main函数执行完毕,对Json::Value root进行析构时,程序便出现了异常。分析下,很显然,调用ModuleClass::MallocMemoryHere时申请的内存,是在module.dll中申请的,而对这些内存的析构则是在应用程序(.exe)中进行的(析构root会同时析构append在root上的所有子Json::Value)。不过,这是异常的真正原因么?

    追踪到异常的出错点:dbgheap.c文件中那句ASSERT语句。

    /*
    * If this ASSERT fails, a bad pointer has been passed in. It may be
    * totally bogus, or it may have been allocated from another heap.
    * The pointer MUST come from the 'local' heap.
    */
    _ASSERTE(_CrtIsValidHeapPointer(pUserData));

    注释中的最后一句话”The pointer MUST come from the ‘local’ heap“引起了我的警惕,难道对于内存的申请和释放不是在同一个heap上,除了‘local’ heap还有一个什么heap么。

    去MSDN上搜索了关于_CrtIsValidHeapPointer,似乎找到了答案,以下这段话是MSDN上对于_CrtIsValidHeapPointer的介绍:

    The _CrtIsValidHeapPointer function is used to ensure that a specific memory address is within the local heap. The local heap refers to the heap created and managed by a particular instance of the C run-time library. If a dynamic-link library (DLL) contains a static link to the run-time library, it has its own instance of the run-time heap, and therefore its own heap, independent of the application’s local heap. When _DEBUG is not defined, calls to _CrtIsValidHeapPointer are removed during preprocessing.

    注意字体加粗的部分,这不正应对我的情形么?!错误不在于DLL中申请的内存在EXE中释放,而在于如果这个DLL拥有一个静态链接,它就会拥有独立的运行时堆,独立于应用程序的堆。这样对于内存申请和释放并不是在同一个堆上进行的,当然出错了。

    解决:虽然MSDN上最后说,如果把项目改成release的,这个ASSERT就将避免,但这是放纵内存泄露,最好的解决办法是将静态链接也改成动态链接,这样就使得DLL能够和应用程序共享同一个堆,错误也得以避免。

    于是,我修改了jsoncpp的项目配置,生成jsoncpp的动态链接库,而不是使用静态库,重新导入到module.dll中,错误解决。

    附jsoncpp编译方法:

    要使用jsoncpp库 我们需要获取到静态链接库或者动态链接库以及相应的头文件 
    下面我们就对这一过程进行详细说明:(windows平台) 
    第一步:到sourceforge下载最新的jsoncpp库http://sourceforge.net/projects/jsoncpp/files/jsoncpp/ 
               目前最新的版本为0.6.0-rc2 
    第二步:解压得到的jsoncpp-src-0.6.0-rc2.tar.gz文件 
               定位到目录jsoncpp-src-0.6.0-rc2makefilesvs71会发现一个jsoncpp.sln的VS工程文件,用VS打开 
    第三步:设置jsoncpp库的runtime library  
                这一步根据自己项目工程需要来设置  我的工程采用的是Multi-threaded Debug DLL (/MDd) 
                设置方法: 
                 1.右键工程properties->Configuration Properties->C/C++->Code Generation 
                 2.在弹出窗口左上方Configuration选择debug  然后将面板中Runtime Library设置为Multi-threaded Debug DLL (/MDd) 
                 3.在弹出窗口左上方Configuration选择release 然后将面板中Runtime Library设置为Multi-threaded DLL (/MD) 
    注意: 
    通过以上设置之后 release版本的库在使用过程中会有编译不通过的问题,解决方法如下: 
    1.右键工程properties->Configuration Properties->C/C++->Output Files 将Assembler Output设置为No Listing 
    2.properties->Configuration Properties->C/C++->Optimization 将Optimization设置为Full Optimization(/ox) 
    然后继续 


    第四步:抽取自己的库 
               1.在想要存便已完成的库的地方如(C:)建立文件夹jsoncpp-0.6.0,并建立子目录jsoncpp-0.6.0lib_jsondebug  jsoncpp-0.6.0lib_json elease 
               2.将工程目录中的jsoncpp-src-0.6.0-rc2include文件夹拷贝至jsoncpp-0.6.0下 
               3.将jsoncpp-src-0.6.0-rc2uildvs71debuglib_jsonlib_json.lib拷贝至jsoncpp-0.6.0lib_jsondebug 
               4.将jsoncpp-src-0.6.0-rc2uildvs71 eleaselib_jsonlib_json.lib拷贝至jsoncpp-0.6.0lib_json elease 
    OK  至此我们需要的库就打包好了jsoncpp-0.6.0 
    使用方法跟一般静态链接库的用法相同

  • 相关阅读:
    实例协议分析RFC1483:AAL5和几种常见ADSL接入技术
    2.2.3 Runaround Numbers
    2.2.2 Subset Sums
    2.2.1 Preface Numbering
    Dynamic Programming
    Data Structures
    2.1.5 Hamming Codes
    2.1.4 Healthy Holsteins
    2.1.3 Sorting a Three-Valued Sequence
    2.1.2 Ordered Fractions
  • 原文地址:https://www.cnblogs.com/mforestlaw/p/3783510.html
Copyright © 2020-2023  润新知