代码之家  ›  专栏  ›  技术社区  ›  Bryce Wagner

marshal.ptrtostructure的globalalloc标志

  •  1
  • Bryce Wagner  · 技术社区  · 14 年前

    短版:

    将句柄从globalalloc(gmem_moveable,size)传递到marshal.ptrtostructure()和marshal.freehglobal()会导致内存损坏吗?

    长版本:

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

    在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(基本上是一个无符号int)放入intptr,并使用marshal.ptrtostructure将数据复制到C结构中:

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

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

    不管怎样,我们设法得到了一些它发生的日志,在这两种情况下,它显示了最近对上面的“marshalrec”函数的调用,一些其他函数调用,然后在delphi线程的事件循环上处理Windows消息(是的,它有自己的线程和事件循环来处理时间敏感的设备驱动程序)。

    因此,我的怀疑落在了GMEM可移动的标志上。我在Marshal类中找不到执行globalLock和globalUnlock操作的任何内容,因此我假设它是由ptrtoStructure()内部处理的。

    ptrtoStructure是否正确处理句柄,或者它是否需要从globalLock()获取实际指针?在非常罕见的情况下,Windows是否可能会移动我分配的内存,这意味着我需要调用globallock()来获取要传入的实际指针?FreehGlobal实际上正在释放一些它不应该释放的东西,这会在下一次访问资源时导致整个应用程序的关闭?

    如果是这样的话,将移动的gmem_改为固定的gmem_是否应该防止这种情况再次发生?还是需要dllimport globallock()和globalunlock()?

    我很想盲目地做出这些改变,但是考虑到这个问题的不可复制性,在它再次发生之前,无法判断它是否被修复。所以我在寻找关于这段代码是否会导致我看到的症状的反馈,或者如果我需要开始想出其他的理论。

    1 回复  |  直到 14 年前
        1
  •  2
  •   Hans Passant    14 年前

    嗯,您明显违反了globalalloc()的合同。您希望marshal.ptrtostructure()将调用globallock是没有根据的,它无法判断传递的intptr是句柄还是指针。

    globalalloc是Windows3.x时代相当过时的传统功能。是的,它很可能将地址作为句柄值返回,globalLock()是一个no-op。但显然没有文档记录这样做。cotaskmemalloc()是更好的鼠标陷阱。