Marshal.PtrToStructure是否指向指向本机C ++堆的指针明智?

问题描述

我有一个使用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)然后尝试在该位置读取内存,但我不知道这是否会导致问题。

我已经读过this,这表明C ++应用程序和CLR都使用相同的堆(即GetProcessHeap),因此这似乎支持了我对Windows 10的发现。与其他操作系统版本不同。

我的示例代码是否理智?这里有陷阱吗?这段代码可以在Windows 7、8和10中运行吗?

解决方法

是的,只要C / C ++代码不释放内存,那是完全合理和安全的。请注意,此处不一定总是(或希望)使用Marshal;根据{{​​1}}是什么,您也可以 以其他方式执行此操作,包括:

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

所有这些都是零拷贝方法,这意味着您的C#代码随后直接与完全相同的内存空间而不是本地快照进行通信;但是如果您将指针(托管或未托管)取消引用非引用本地中,则它将进行复制。