【开发日记】调用方释放DLL中申请的内存

13 篇文章 1 订阅

问题描述:C#调用C++的dll,传入一组数据,返回处理后的数据以及一些信息字段。但是,返回的内容是不定长的,也就是dll内部要进行动态内存申请。那么问题来了,C#怎么接收一个大小不确定的变量?使用完这些数据后,怎么在C#里面释放其内存?


前一个问题的危险是,该段内存的大小,调用方事先是未知的不能进行初始化,这可能会导致缓冲区溢出问题。目前的解决方法只能够是调用方预先分配一个足够大的内存空间,提供给返回数据,但返回数据的大小必须小于预分配的内存空间;如果大于怎么办?无能为力,增大电脑内存吧


后一个问题,貌似也是一个比较棘手的问题,特别是在跨平台上。

看到老外们的这样一段讨论:http://forums.codeguru.com/showthread.php?229394-memory-allocations-in-dll

里面有一段陈述

If you are using the DLL version of the C runtime library, you won't get an error, since your app and DLL are using the same heap. 
Change your project settings to use the non-DLL version of the runtime library. You will be using two different heap managers -- you can't create a pointer from one heap manager and delete it with another heap manager. Your program will crash, if not now, eventually it will. 
This is a well-known problem for years, and has been written about in various articles, most notably in the C/C++ User's Journal a couple of years ago when discussing VC++ and STL (Sorry, I can't find the article). 
Also note the many posters on CodeGuru that have come across this problem (there must be at least 50 or so threads dealing with this topic). The remedy is to either use the DLL version of the CRT, or restructure the code so that only one module is responsible for memory allocation / deletion.

大概意思是,不同的运行库决定不同的堆管理方式,所申请的内存必须要由对应的释放方法释放。最后的建议是,在一个模块(dll)中申请的内存,就要在该模块中释放。

这样做很符合封装的原则,但是我的应用需求决定了我不得不违背该原则,这也是我非常头疼的地方。到底有没有什么技术可以完美解决这种问题?

一个国人的观点:http://blog.csdn.net/wzx19840423/article/details/6535785

这里又找到一个老外与其观点暗合:http://www.gamedev.net/topic/289896-deleting-memory-from-dlls-c/

When you have static linking between the exe and dll, they maintain seperate heaps and I believe only, the owner of the heap can allocate or delete memory from the heap. That's why you need the new and delete to be executed from the same module. 
That's not the case though if you dynamically link the dll with the exe (using the LoadLibrary at runtime) since in this case both the exe and the dll share the same heap. In this scenario you should not be having problems if you have the new and delete across module boundaries. 

如果dll是静态(隐式)调用,必须在dll内部申请释放;如果dll是动态(显式)调用,可以再exe中释放。


自己想了一下,opencv不就是这样的吗?里面的函数成千上万,都是C++和C。平时调用,经常是创建一个空的Mat,函数返回一个与输入大小一致的Mat,这个过程就经过dll内部给Mat申请内存,exe中通过Mat的析构函数释放内存。这里有个潜在条件:dll内部的申请方式与exe中的释放方式是对应的,都是Mat类的成员函数。

在运行时,C#与C++的dll是否共用一套堆?不清楚。

暂且只能这样做:dll里定义一个释放内存的接口,C#调用接口释放内存,该释放动作必定发生在dll的堆上,故可以成功释放。

有个潜在问题:如果C#与DLL的堆不一致,那么DLL内部的该段内存是释放了,C#中呢?甚至是,DLL内部申请的内存,到了C#中也根本不认!

这只能实践了


经过几次的纠缠,终于解决了C#与C++的交互问题,还是那一句:实践出真知!

解决办法:

1、通过传递指针的地址值而非指针本身,返回输出变量

原来一直报错,是因为函数的参数中含有指针,把这个指针以整型变量int的形式传入函数就不会报错,而且通过这个int同样可以实现内存的释放操作。

这里再总结一下C#封送结构体变量的注意事项:

1)结构体的定义,C#与C++必须完全一致;

2)结构体成员越简单越好,最好全部是int;

3)要想在函数中修改结构体成员的值,必须在C#中用ref(反射),C++中用*(结构体指针)来表示该结构体形参;

4)遇到指针,就用强制类型转换 (int) 来获取其地址值再传入函数

以上几点没有做好,C#中都会报错,错误为“内存损坏”或者“找不到指定模块”


2、C++附带编写一个释放内存的接口函数,C#可以调用该函数释放在dll内部申请的内存


  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值