指向本机 C++ 堆的指针上的 Marshal.PtrToStructure 是否正常?

Is Marshal.PtrToStructure on pointers that point to the native C++ heap sane?

我有一个使用 PInvoke 在 C# 和 C++ 之间封送数据的应用程序。该应用程序是一个本机 C++ 应用程序,它在内部使用 C# 部分启动 CLR。​​

有时我必须将数据从 C++ 编组到 C#,为此我使用 Marshal.PtrToStructure。 C++部分基本上是这样的:

struct X {};

auto xPtr = new X; // somewhere in the application

callCSharp(xPtr); // somewhere else

C# 部分是这样的:

public void callCSharp(IntPtr xPtr)
{
    var x = Marshal.PtrToStructure<X>(xPtr);
}

此代码可在我的 Windows 10 机器上运行,但我不确定是否 运行 使用此代码会遇到麻烦。生命周期管理全部在 C++ 中完成,因此我不必在 C# 端分配或释放任何东西。但是我不确定 CLR 是否总是可以访问本机堆。 xPtr 是使用 new 分配的,因此位于 C++ 应用程序的本机堆上。 Marshal.PtrToStructure<X>(xPtr) 然后尝试读取该位置的内存,但我不知道这是否会导致问题。

我读过 ,它表明 C++ 应用程序和 CLR 使用相同的堆(GetProcessHeap 即),所以这似乎支持我对 Windows 的发现10,但其他 OS 版本可能有所不同。

我的示例代码是否正常?这里有什么陷阱吗?此代码在 Windows 7、8 和 10 中有效吗?

是的,只要 C/C++ 代码不释放内存,这是完全合理和安全的。请注意,在这里使用 Marshal 并不总是必要的(或可取的);根据 <X> 是什么,您 也可以 以其他方式执行此操作,包括:

  • unsafe(将 void* 转换为 X*
  • Unsafe.AsRef<X>(...)(将 void* 转换为 ref X
  • new Span<X>(...)(从 void* 创建一定数量的 X 的跨度;跨度就像一个向量,但与任意内存对话)

所有这些都是 zero-copy 方法,这意味着您的 C# 代码然后直接与完全相同的内存对话 space,而不是本地快照;但是如果你 取消引用 指针(托管或非托管)到 non-reference 本地,那么它会制作一个副本。