关于DLL接口处传入string类型在其析构时报堆损坏的问题
来源:互联网 发布:淘宝买普通话二甲证书 编辑:程序博客网 时间:2024/05/09 08:45
最近已经遇到这个问题两次了,看了网上一些资料了解了下,引用两篇过来,以免忘记
文章一:出处http://hi.baidu.com/anowsober/blog/item/9c1abcd95d20b4ee38012f6b.html
本剧关键字:exe、dll、STL string、local heap。
情景:一个结构体 ITEM 中包含了多个 string 类型字段,在 exe 中的 ExeFunc 中定义 ITEM 变量 item,传 item 的引用到 DllFunc 函数中,DllFunc 是一个 dll 的导出函数,负责从文件中读取一些数据到 ITEM 结构中。在 ExeFunc 结束的时候 string 异常了,提示堆内存损坏。排除法发现多个 string 字段中只有一个引发了异常,其它的都没有问题,觉得很奇怪。string 异常的字段赋值的字符串大于 16 个字符(string 默认是15 还是 16 个 byte 来着)其它的都小于 16 个字符,显然是 string 对象重新分配内存了。
查看内存也没有发现异常情况,怀疑 string 有问题,于是另外建了个工程测试 string,单个 exe 或者模拟工程中建立一个 dll 赋值给引用 string 变量都正常,而且字符数大于 16 个。为什么为什么为什么呢?同样是重新分配了内存,差别怎么就这么大捏。
跟踪定位到 _CrtIsValidHeapPointer ,注意到 dbgheap.c 文件中 _CrtIsValidHeapPointer 处注释:
/*
* 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));
搜索到一些关于本地堆(local heap)的文章,才明白是因为 dll 如果静态链接了运行时库,dll 就会拥有独立于应用程序堆(也称作local heap)的运行时堆实例。此时在 dll 外部就不能访问此 local heap,所以也就有上面所出现的异常啦。MSDN 中也有介绍:
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 时动态链接运行时库( VS2005 下动态链接 MFC 库默认就是动态链接运行时库,如果更改静态链接 MFC 库,运行时库的链接方式也跟着改变为静态的),并且 exe 中也要使用相同的链接方式才行。这或许是有时候改变 MFC 库的链接方式会引起一些莫名错误的原因吧;对了在 _CrtIsValidHeapPointer 内部也有这样的注释:
/*
* Go through the heap regions and see if the pointer lies within one
* of the regions of the local heap.
*
* Pointers from non-local heaps cannot be handled. For example, a
* non-local pointer may come from a DLL that has the CRT linked-in.
*
*/
结论:应用程序使用的 dll 库在链接时尽量使用相同的链接方式,大多数情况下不同的链接方式不会出现问题,但有少数情况总是存在滴。俺要是在第一时间看清楚明了的 MSDN 就不会走N多的弯路了555(在弯路上也认识了不少东西算是弥补吧呵呵)。
文章二:出处http://topic.csdn.net/u/20091202/09/999d3d15-cec4-4e40-8ad3-e1f35442d9ef.html
动态库接口层使用STL的运行异常及一个解决思路
在内部使用一下无所谓,但是在动态库接口层使用,会有问题。比如我的动态库有接口如下:
- C/C++ code
- class __declspec(dllexport) FDAClient_Helper{ bool get_child_path(vector<string> &child_path, const string &parent);}
我们假定这个接口的作用是根据输入的目录路径,查找目录包含的所有子目录。
如果是静态编译,也就是没有__declspec(dllexport)。编译为一个lib文件。这样供exe直接链接,那么运行是没有问题的。
但是如果我们动态编译为一个dll和lib导入库。那么运行的时候几乎是100%抛异常的。异常是发生了child_path使用后,自动析构的时候。
原因分析:STL是模板编码的,实际编码后,dll的运行空间和exe的运行空间并不在一起。而child_path在exe这边声明,然后在dll里面分配内存,最后在exe这边释放内存。
首先说一个不优雅的解决方式:
修改接口
- C/C++ code
- class __declspec(dllexport) FDAClient_Helper{ bool get_child_path(vector<string> &child_path, const string &parent); void destroy(vector<string> &vec_str);}//////////////////////////////////////////////////////////////////////////void FDAClient_Helper::destroy(vector<string> &vec_str){ vec_str.clear();}
每次使用child_path,在系统会自动析构它之前,首先调用destroy(child_path).
这样就实现了,在dll这边申请空间,同时也在dll这边释放空间。
唯一不爽的就是每次需要显示调用destroy,很不优雅。
再说一个稍微优雅的方法:
以string为例
- C/C++ code
- class __declspec(dllexport) smart_string { public: smart_string(); ~smart_string(); const char *get_data() const; void set_data(const char *data); void clear(); smart_string & operator += (const char *data); private: std::string *data; }; smart_string::smart_string() { data = new std::string(); } smart_string::~smart_string() { if (data) { data->clear(); delete data; } } const char * smart_string::get_data() const { return data->c_str(); } void smart_string::set_data(const char *_data) { data->clear(); data->replace(0, data->size(), _data); } void smart_string::clear() { data->clear(); } smart_string & smart_string::operator += (const char *_data) { *data += _data; return *this; }
这样在凡是需要string的地方,都用smart_string代替,exe中每次使用smart_string以后,出了作用域,系统会自动析构smart_string。而dll空间会使用~smart_string会正确释放内存的。
vector等都可以使用这种方式。这样使用不会有使用STL的麻烦,却有使用STL的便利。
希望高手能够提供更加优雅的写法。当然最好的方法还是在接口层不使用类,只是有结构体和简单变量。
- 关于DLL接口处传入string类型在其析构时报堆损坏的问题
- mybatis中传入String类型参数的问题
- 在使用mybaitis传参数的时候,仅传入一个类型为String的参数所遇到的问题
- 关于Dll中导出string的问题
- 对于mybatis传入string类型的参数
- mybatis 传入String 类型的参数
- 关于vs2010下编译dll动态库,JNA接口在java中调用的问题
- 关于String类型变量的比较问题
- 关于BigDecimal在MySQL和Oracle中设计表的时候如何设计其类型的问题
- 关于在Eclipse 开发中针对String类型中文显示的问题
- dll接口中使用string问题
- 关于mybatis 传入基本参数类型使用if test 判断出错的问题
- C++ 在DLL中使用std::string乱码的问题
- Windows 已在 XXX.exe 中触发一个断点。 其原因可能是堆被损坏,这说明 FiberSecureTerminal.exe 中或它所加载的任何 DLL 中有
- 关于malloc分配内存的“堆被损坏“Bug
- 解决C++/CLI中关于“MissingManifestResourceException类型的未经处理的异常出现在mscorlib.dll”问题一例
- 关于运行String parentId = Util.createUUID()时报错InvocationTargetException的问题
- “堆被损坏”这个让人崩溃的问题
- C# 对sharepoint 列表的一些基本操作,包括添加/删除/查询/上传文件给sharepoint list添加数据
- 【经典推荐】每个初学者都应该搞懂的问题
- VS2010各种版本资源下载汇总
- 为了下载资源
- ZOJ 省赛题 An Awful Problem
- 关于DLL接口处传入string类型在其析构时报堆损坏的问题
- Java 多线程与并发编程专题
- git 管理 内核
- KM algo
- java.util.current
- 递归和循环效率比较
- 4月6日参加CTO俱乐部组织的研发绩效考核论坛
- String.Format()的用法
- 简单的杨辉三角