本文转自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-rc2\makefiles\vs71会发现一个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.0\lib_json\debug  jsoncpp-0.6.0\lib_json\release 
           2.将工程目录中的jsoncpp-src-0.6.0-rc2\include文件夹拷贝至jsoncpp-0.6.0下 
           3.将jsoncpp-src-0.6.0-rc2\build\vs71\debug\lib_json\lib_json.lib拷贝至jsoncpp-0.6.0\lib_json\debug 
           4.将jsoncpp-src-0.6.0-rc2\build\vs71\release\lib_json\lib_json.lib拷贝至jsoncpp-0.6.0\lib_json\release 
OK  至此我们需要的库就打包好了jsoncpp-0.6.0 
使用方法跟一般静态链接库的用法相同

小心DLL链接静态库时的内存错误的更多相关文章

  1. Unix环境链接静态库

    静态库 请点评 有时候需要把一组代码编译成一个库,这个库在很多项目中都要用到,例如libc就是这样一个库,我们在不同的程序中都会用到libc中的库函数(例如printf),也会用到libc中的变量(例 ...

  2. Qt5.7中使用MySQL Driver(需要把libmysql.dll文件拷贝到Qt的bin目录中。或者自己编译的时候,链接静态库)

    Qt5.7中使用MySQL Driver 1.使用环境 Qt5.7的安装安装就已经带了MySQL Driver,只需要在安装的时候选择一下即可.如果没有安装,可以采取自己编译的方式.在Qt的源码包的q ...

  3. linux下 GCC编译链接静态库&动态库

    静态库 有时候需要把一组代码编译成一个库,这个库在很多项目中都要用到,例如libc就是这样一个库, 我们在不同的程序中都会用到libc中的库函数(例如printf),也会用到libc中的变量(例如以后 ...

  4. cmake 强制链接静态库

    add_executable(main main.cpp) target_link_libraries(main ${CMAKE_SOURCE_DIR}/libbingitup.a) 静态库和动态库共 ...

  5. 【VS开发】Caffelib中出现的问题:强制链接静态库所有符号(包括未被使用的)

    C++程序在链接一个静态库时,如果该静态库里的某些方法没有任何地方调用到,最终这些没有被调用到的方法或变量将会被丢弃掉,不会被链接到目标程序中.这样做大大减小生成二进制文件的体积.但是,某些时候,即使 ...

  6. 编译skia静态库时,图片解码库无法注册的问题

    转载:http://www.cnblogs.com/imlucky/archive/2012/08/01/2617851.html 今天编译skia库,增加图片解码库时总是无效.按照此博客的方法修改后 ...

  7. opencv编译静态库时选择MD模式无效的原因

    在Cmake-gui上看到的明明是MD运行库依赖,生成MS项目时却变成了MT运行库依赖. 原因在于编译静态库时内部做了自动替换.

  8. Linux:编译动态库时遇到的错误relocation R_X86_64_32 against `a local symbol'

    编译动态库时遇到如下错误: ... ... relocation R_X86_64_32 against `a local symbol' can not be used when making a ...

  9. VC运行库版本不同导致链接.LIB静态库时发生重复定义问题的一个案例分析和总结

    转帖:http://blog.csdn.net/whygosofar/article/details/2821875 MSDN中对于在不同的配置下Link的LIB作了说明: C Runtime Lib ...

随机推荐

  1. js 中的 exec( )方法

    JavaScript exec() 方法 JavaScript RegExp 对象 定义和用法 exec() 方法用于检索字符串中的正则表达式的匹配. 语法:RegExpObject.exec(str ...

  2. [Q]图框识别问题

    一个CAD文件可能包含很多张(页)图,每张图通常包含在一个图框里,这个图框通常是矩形的. 打图精灵识别图框所在区域,然后打印该区域,打图精灵识别图框实际上是识别最外侧的矩形(无矩形打印见下文) 如A矩 ...

  3. Python的加入!

    今天有幸领略了Python的风采. 真是好清新>_< 赶紧尝试一下. 好酷. 以后会在项目中使用

  4. Mac Java maven环境变量

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000; background-color: #fffff ...

  5. Base algorithm

    今天介绍几种常见的算法,在面试中或许能派上用场 1.字符串倒转 //Reverse a string public static string Reverse(string ori) { string ...

  6. C#中值类型与引用类型通俗理解

    关于值类型和引用类型已经有很多人写了很多文章,但是很多人也只是停留在字面上的理解,如果采用一种通俗的方法来解释,想必很多人都会理解.我们都知道值类型存储在栈上,引用类型存储在堆上,引用类型都是xxx类 ...

  7. JavaScript DOM编程艺术-学习笔记(总结一)

    1.1)dom-core方法:(不专属于js,支持dom的任何一种程序设计语言都可以使用它,它们的用途,也不仅限于处理网页,也可以用来处理任何一种标记语言编写处理的文档)  ①getElementBy ...

  8. oAuth 使得第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权

    OAUTH协议为用户资源的授权提供了一个安全的.开放而又简易的标准.与以往的授权方式不同之处是OAUTH的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可 ...

  9. we are happy 把空格换成 %20 剑指offer P44

    public class StringReplace { public static void replaceSpace(String[] str, int length) { if(str == n ...

  10. Linux常用命令汇总及使用方法(三)

    (1)文件管理命令 ls.ls -l.ll.ls -a ls -a 显示所有文件,包括隐藏文件 ls 列举指定目录下的子目录和文件 ls -l与ll相同 列举指定目录下的子目录和文件的详细信息 tot ...