【问题标题】:C# P/Invoke [DllImport] - array memory managementC# P/Invoke [DllImport] - 数组内存管理
【发布时间】:2011-07-11 14:34:40
【问题描述】:

我将 byte[] 传递给接受 unsigned char* 的函数

我可以做到这一点的一种方法是传递一个 IntPtr,并在托管代码中分配/取消分配内存,如下所示:

  • 在 C++ DLL 中

    extern "C" 
    {
        __declspec(dllexport) void __stdcall Foo(int length, unsigned char** message);
    }
    
  • 在 C# 中

    [DllImport(@"MyDll.dll"]
    public static extern void Foo(int length, ref IntPtr msg);
    
    byte[] msg = new byte[] {0,1,2,3};
    IntPtr ip = Marshal.AllocHGlobal(msg.Length);
    Marshal.Copy(msg, 0, ip, msg.Length);
    UnmanagedCode.Foo(msg.Length, ref ip);
    Marshal.FreeHGlobal(ip);
    

我也可以这样做:

  • 在 C++ DLL 中

    extern "C" 
    {
        __declspec(dllexport) void __stdcall Foo(int length, unsigned char* message);
    }
    
  • 在 C# 中

    [DllImport(@"MyDll.dll"]
    public static extern void Foo(int length, byte[] msg);
    
    byte[] msg = new byte[] {0,1,2,3};
    UnmanagedCode.Foo(msg.Length, msg);
    

两种实现都运行良好,但在我的第二个示例中,内存(对于 unsigned char* 消息)是如何管理的。我猜内存是在调用 Foo 时分配的,并在它返回时释放(所以它的行为很像第一个示例) - 这是正确的吗?

谢谢

【问题讨论】:

  • 我不明白为什么你会在第一个例子中传递ref ip。您不可能更改ip 的值。在第二个示例中,unsigned char message[] 的读取效果要好得多。最后,如果您要传递 IP 地址,结构体将是可行的方法——不需要长度参数。

标签: c# arrays memory-management pinvoke dllimport


【解决方案1】:

第二种情况没有进行管理。 GC 不计算从非托管代码完成的引用。当外部函数不能与线程一起使用或在以后的函数调用中使用引用时,这很好。
当您使用委托/函数指针和非托管代码时,会出现类似的问题,您可能会遇到这种情况代表在随机点被解除分配。 这个MSDN article 很高兴为您解释所有细节。

【讨论】:

  • 我知道不涉及 GC(也许我不应该使用“管理”一词),但必须有一些东西将数据分配并复制到非托管堆上——毕竟,我能够在 Foo 期间访问数组。这是什么?
  • 分配只是 CLR 的一部分。看看这篇文章,它很好地解释了细节,例如什么时候固定内存,什么时候复制。
  • 谢谢 - 我错过了您上一篇文章中 MSDN 文章的链接。在运行了一些测试之后,我发现第二个实现要快得多,所以我会坚持下去。
猜你喜欢
  • 2023-03-08
  • 2016-03-15
  • 1970-01-01
  • 1970-01-01
  • 2022-06-25
  • 1970-01-01
  • 2013-05-31
  • 2011-07-20
  • 1970-01-01
相关资源
最近更新 更多