在哪些情况下,执行P/Invoke调用时应固定参数

本文关键字:调用 参数 Invoke 情况下 执行 | 更新日期: 2023-09-27 18:27:06

我有一个DLL,我需要从中p/Invoke以下C方法:

int DAOpen(HANDLE *hOpen, UNIT *flags, void *callback, char *userData)

我想出了以下C#签名:

[DllImportAttribute("<libName>", EntryPoint="DAOpen")]  
    static extern  int DAOpen(  
    out IntPtr hOpen,  
    ref uint flags,  
    IntPtr callback,  
    IntPtr userData);

假设本机代码保持对所有参数的引用的时间长于p/Invoke调用的持续时间:

  1. 除了保留hOpen的一个实例之外,我还应该固定它吗?

  2. 我应该保留flags变量的引用吗?既然它在这种特殊情况下被作为引用传递,我是否也应该将其固定?

  3. 我以以下方式分配我的callback代表:

    private IntPtr callBackOnNativeEvents;
    ...
    this.callBackOnNativeEvents = Marshal.GetFunctionPointerForDelegate(
    new CallBack(this.CallBackOnNativeEvents));

    我应该保留对委托本身的引用(而不仅仅是指针)吗?我应该也把它钉上吗?

  4. 最后,我以以下方式定义userData参数:

    private IntPtr userData;
    ...
    string userName = "test";
    this.userData = Marshal.StringToHGlobalAnsi(userName);

    我应该保留对字符串的引用吗?我应该也把它钉上吗?API文档指出它将字符串内容复制到非托管内存中,但我不确定它是否复制了引用的内容。

在哪些情况下,执行P/Invoke调用时应固定参数

  1. 无需引脚hOpen,它具有值类型语义
  2. 如果DLL写入flags指向的地址,并且在原始函数返回后这样做,那么您需要以某种方式固定它(以及保持它的活动性和安全性,使其免受GC的控制)
  3. 回调函数指针已被有效固定。您需要保持对委托的引用处于活动状态,但不需要固定它,因为本机thunk是从未管理的堆中分配的
  4. 你不需要在这里做任何特别的事情,因为你正在传递一个IntPtr,而它后面的内存是固定的。您不需要保持对字符串的引用处于活动状态,因为它与StringToHGlobalAnsi返回的IntPtr完全断开连接。它只是在调用StringToHGlobalAnsi时有一个字符串内容的副本

我不得不说,我仍然不相信这个DLL真的可以做你说它正在做的事情。我怀疑还有其他问题,您错误地诊断为DLL保留了一个调用中的指针参数,然后在随后的调用中修改了它们的内容。我觉得这很难相信,但当然只有你才能真正知道。如果我处于你的位置,我会简单地问DLL供应商的问题。