.net 使用P/Invoke编组IntRef

.net 使用P/Invoke编组IntRef,.net,pinvoke,.net,Pinvoke,当我使用IntPtr保留内存并将动态数组传递给本机代码时,在C#/managed端初始化该内存并将其传递给本机DLL后,该内存块是固定的还是复制的?也就是说,如果我在本机代码中修改数组,我会在托管代码中看到修改吗 我知道没有必要使用IntPtr,但由于数组嵌入到复杂结构中,因此似乎更方便。在.NET程序中获得内存分配的IntPtr的唯一有效方法是: 通过使用Marshal.AllocXxx()方法之一。这不是固定内存,而是从一个操作系统堆中分配的不可移动内存。一般来说,操作系统不支持更改分配地

当我使用IntPtr保留内存并将动态数组传递给本机代码时,在C#/managed端初始化该内存并将其传递给本机DLL后,该内存块是固定的还是复制的?也就是说,如果我在本机代码中修改数组,我会在托管代码中看到修改吗


我知道没有必要使用IntPtr,但由于数组嵌入到复杂结构中,因此似乎更方便。

在.NET程序中获得内存分配的IntPtr的唯一有效方法是:

  • 通过使用Marshal.AllocXxx()方法之一。这不是固定内存,而是从一个操作系统堆中分配的不可移动内存。一般来说,操作系统不支持更改分配地址的概念,一旦分配完成,它将永远停留在同一地址。只有垃圾收集器具有必要的魔力,它们才能找到指向内存块的指针,并知道如何更新它们
  • 通过使用GCHandle.AddRofPindedObject()。从GC堆分配,地址将保持稳定,直到调用GCHandle.Free()。仅当分配需要在短时间内存在时才使用它
  • 通过使用C#fixed关键字。这是一个高度优化的GCHandle版本,无需分配句柄即可工作。通过抖动将变量标记为特殊变量来实现,GC在遍历堆栈寻找根时将发现该变量。否则,它的效果与GCHandle完全相同,GC认为固定语句的目标已固定,不会移动该对象

对后两个项目要非常小心,尤其是固定关键字,它需要不安全,因为它是危险的。一旦代码执行离开固定语句的范围,停止使用IntPtr是非常重要的。当GC发现堆损坏时,故障模式不是由运行时强制的,而是完全不可诊断的ExecutionEngineeException。由于在内存块移动后通过IntPtr将代码写入内存块,从而覆盖了其他内容而导致的。

您是说
封送。AllocHGlobal
?它分配本机不可移动内存块并返回指向它的
IntPtr
。如果您将这个
IntPtr
传递给本机函数,并且它更改了内存,C#可以看到这些更改。内存不是自动释放的,请使用
Marshal.FreeHGlobal
进行此操作。是的,我认为这回答了我的问题。因此,使用
marshall.AllocHGlobal
分配的内存始终是固定的。是的,这是本机内存,GC不能使用它。这取决于您如何分配内存。你没有说。固定内存和从操作系统堆分配的内存有什么区别?固定内存是否属于托管堆?但我想在这两种情况下,我在本机代码中所做的更改都可以在托管代码中看到。此外,
stackalloc
和P/Invoke可以调用任何数量的分配内存的本机API。此外,
SafeBuffer
和派生类型也会公开
DangerousGetHandle()