【问题标题】:Incorrect interop signature causing memory leak不正确的互操作签名导致内存泄漏
【发布时间】:2011-08-05 12:36:24
【问题描述】:

通过互操作生成的接口从 c# 使用的第 3 方 com 模块正在泄漏内存

第3方c++方法签名为:

somemethod(....., long** param3, long** param4)

生成的互操作方法是:

somemethod(...., IntPtr param3, IntPtr param4)

最后 2 个参数由 umanaged dll 分配数组,并从 c# Marshal.CoMemFree 中释放(不记得确切的 sig atm)

通过 com 接口使用 C++ 中的相同方法并以相同方式释放不会产生泄漏

从命令行使用 tlbimp 会产生:

TlbImp : warning TI0000 : At least one of the arguments for
'Sometype.somemethod' cannot be marshaled by the runtime
marshaler. Such arguments will therefore be passed as a pointer and may
require unsafe code to manipulate.

我发现很长的**参数无法自动编组令人惊讶。

比.net更懂c++(不包括com黑魔法),但是实现.net端...

访问和释放在 param3 和 param4 中传回的内存的正确方法是什么。我怀疑他们应该是“out IntPtr”?

【问题讨论】:

    标签: .net c++ memory-leaks interop marshalling


    【解决方案1】:

    此声明与 COM 自动化严重不兼容。数组需要作为 SAFEARRAY 传递,因此非常清楚它们有多大以及它们的内存是如何管理的。传递一个 long** 将通常表明被调用者负责分配数组并返回指向数组的指针。 究竟应该如何分配是个问题,不清楚是应该使用进程堆、COM堆还是可以使用CRT堆。

    Tlbimp.exe 抛出一个错误,它不知道如何正确转换参数类型。您必须使用 ildasm.exe 反编译互操作库,编辑 IL 以将参数转换为 out IntPtr 或 out int[],然后使用 ilasm.exe 再次编译。您对分配器的唯一合理猜测是 Marshal.AllocCoTaskMem()。可能会工作,可能会严重泄漏。您需要组件供应商或作者的帮助以避免猜测。

    【讨论】:

    • 仅粗略了解您的分析器,要点是它看起来并不乐观。第 3 方和负责释放的调用者记录了“被调用者负责分配数组”。 tlbimp生成的接口一定要用吗?是否可以用正确的类型手动编码接口? Alternativley 的想法是编写一个包装 dll,通过 PInvoke 公开一个 c 接口,不知道这有多少杂乱无章...
    • '你对分配器的唯一合理猜测是 Marshal.AllocCoTaskMem()。可能会工作,可能会严重泄漏。您需要组件供应商或作者的帮助以避免猜测。 - 您是否建议在调用本身在非托管 dll 中分配实际数组的方法之前,需要在 c# 端分配 mem?
    • 是的,long** 表示被调用者必须分配数组。只要您在 C# 中实现代码,您就确实需要互操作库。你必须编辑它。用 C++/CLI 编写代码当然是一种选择,使用 #import 导入类型库并获取 _com_ptr_t 智能指针。但是,您仍然不知道如何正确分配数组。
    • 库分配数组,需要客户端/调用者释放它,可能在OP中不清楚。如何获取分配数组的地址是难点。
    【解决方案2】:

    找到了获得预期功能的“一种”方法,但不确定该解决方案的效果如何。跟踪工作不泄漏

    long* arr1 = null;
    long* arr2 = null;
    
    IntPtr parr1 = new IntPtr(&arr1);
    IntPtr parr2 = new IntPtr(&arr2);
    
    somemethod(....., parr1, parr2);
    
    Marshal.CoTaskMemFree(new IntPtr(arr1));
    Marshal.CoTaskMemFree(new IntPtr(arr2));
    

    注意事项:

    1. 没有尝试访问数组,我实际上并不需要内容,但我想这样做可能需要 Marshall.Copy 调用。

    2. 直觉上调用应该是某种方法(....., ref parr1, ref parr2);然而看起来 IntPtr 实际上更像是一个 void 指针,所以虽然它是按值传递的,但它的值是 arr1 的地址,所以被调用者能够分配给 arr1,闻起来不对,但它可以工作,可能 arr1 或 parr1 应该被分配Marshal.CoTaskMemAlloc (?)

    3. 我之前在控制台应用程序中尝试过上述操作,但它仍然泄漏,但是当指定 [STAThread](而不是指定/默认)时,泄漏停止。事实上,公寓的事情改变了代码的含义,所以微妙的臭味

    【讨论】:

      猜你喜欢
      • 2021-03-04
      • 1970-01-01
      • 2017-01-06
      • 2011-08-05
      • 1970-01-01
      • 2015-07-06
      • 2014-06-07
      • 2013-11-20
      • 2011-10-28
      相关资源
      最近更新 更多