Marshal.PtrToStructure的GlobalAlloc标志

时间:2010-11-18 18:01:12

标签: c# interop marshalling

简短版本:

可以将GlobalAlloc(GMEM_MOVEABLE,Size)的句柄传递给Marshal.PtrToStructure()和Marshal.FreeHGlobal()会导致内存损坏吗?

长版:

我正在使用Windows全局内存分配在Delphi和C#应用程序之间来回传递数据结构(事实上它的Delphi对这个问题并不重要,因为它只是Win32 API调用)。 p>

在Delphi端,我传入一条记录,它分配空间,锁定内存,将结构复制到内存中,并解锁内存:

function MarshalRec(SourceRec: TInteropItemRec): THandle;
var
  Size: integer;
  Buffer: Pointer;
begin
  Size := sizeof(SourceRec);
  result := GlobalAlloc(GMEM_MOVEABLE and GMEM_ZEROINIT, Size);
  Buffer := GlobalLock(result);
  try
    CopyMemory(Buffer, @SourceRec, Size);
  finally
    GlobalUnlock(result);
  end;
end;

在C#端,它将THandle(基本上是一个无符号整数)转换为IntPtr并使用Marshal.PtrToStructure将数据复制到C#结构中:

public void FromMemory(IntPtr Source)
{
    Marshal.PtrToStructure(Source, this);
    Marshal.FreeHGlobal(Source);
}

我遇到的问题非常少(对于我来说,6个月内有4次),整个应用程序都会关闭(此应用程序遇到错误并且必须关闭)。如果我尝试在Visual Studio中暂停执行,我会收到“发生致命错误并需要终止调试。有关详细信息,请参阅Microsoft帮助和支持网站.HRESES = 0x80131c08。”

无论如何,我们设法得到它的几个日志,并且在这两种情况下,它显示最近调用上面的“MarshalRec”函数,一些其他函数调用,然后在事件上处理一些Windows消息在Delphi线程上循环(是的,它有自己的线程和事件循环来处理时间敏感的设备驱动程序)。

所以我怀疑是GlobalAlloc的GMEM_MOVEABLE标志。我在Marshal类中找不到GlobalLock和GlobalUnlock的东西,所以我假设它是由PtrToStructure()内部处理的。

PtrToStructure是否正确处理句柄,还是需要从GlobalLock()获得的实际指针?是否有可能在极少数情况下,Windows碰巧移动我分配的内存,这意味着我需要调用GlobalLock()来获取传入的实际指针? FreeHGlobal实际上是在释放它不应该拥有的东西,这会在下次访问资源时关闭整个应用程序?

如果是这样,是否应该将GMEM_MOVABLE更改为GMEM_FIXED以防止再次发生这种情况?或者我需要DllImport GlobalLock()和GlobalUnlock()?

我很想盲目地做出这些改变,但考虑到这个问题的不可重现性,没有办法判断它是否已经修复,直到它再次发生。因此,我正在寻找有关此代码是否可能导致我所看到的症状的反馈,或者我是否需要开始提出其他理论。

1 个答案:

答案 0 :(得分:2)

嗯,您明确违反了GlobalAlloc()的合同。你希望Marshal.PtrToStructure()调用GlobalLock是没有根据的,它无法判断传递的IntPtr是句柄还是指针。

GlobalAlloc是Windows 3.x时代的一个相当无可救药的过时遗留功能。是的,很可能将地址作为句柄值返回,而GlobalLock()是一个无操作。但肯定没有记录这样做。 CoTaskMemAlloc()是更好的鼠标陷阱。