我有以下结构:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct WAVEHDR
{
    internal IntPtr lpData;   // pointer to locked data buffer
    internal uint dwBufferLength; // length of data buffer
    internal uint dwBytesRecorded; // used for input only
    internal IntPtr dwUser;   // for client's use
    internal uint dwFlags;   // assorted flags (see defines)
    internal uint dwLoops;   // loop control counter
    internal IntPtr lpNext;  // reserved for driver
    internal IntPtr reserved;  // reserved for driver
}

我需要分配非托管内存来存储上述struct的实例。指向该结构的指针将传递给waveOut win32 api函数(waveOutPrepareHeader,waveOutWrite,waveOutUnprepareHeader)。
  • 我应该使用Marshal.AllocHGlobal()还是Marshal.AllocCoTaskMem()?有什么区别?
  • 我应该将sizeof(WAVEHDR)Marshal.SizeOf(typeof(WAVEHDR))传递给内存分配方法吗?有什么区别?

  • 注意,必须固定分配的内存。

    最佳答案

    Windows程序始终至少有两个堆在其中分配了非托管内存。首先是默认进程堆,Windows在需要代表程序分配内存时使用它。第二个是COM基础结构用来分配的堆。 .NET P/调用编码器假定此堆由其功能签名需要取消分配内存的所有非托管代码使用。

    AllocHGlobal从进程堆分配,AllocCoTaskMem从COM堆分配。

    每当编写非托管互操作代码时,都应始终避免分配非托管内存的代码与释放非托管内存的代码不同的情况。很可能使用了错误的解除分配器。对于与C/C++程序互操作的任何代码尤其如此。这样的程序具有自己的分配器,该分配器使用由CRT在启动时创建的自己的堆。用其他代码取消分配这种内存是不可能的,因为您不能可靠地获取堆句柄。这是P/Invoke问题的一个非常常见的来源,尤其是因为XP和更早版本中的HeapFree()函数默默地忽略了对未在正确的堆中分配的空闲内存的请求(释放分配的内存),但是Vista和Win7崩溃了程序除外。

    在您的情况下,无需担心这一点,因为您使用的mmsystem API函数是干净的。它们旨在确保分配的相同代码也可以解除分配。这是您必须调用waveInPrepareHeader()的原因之一,它会使用最终将其取消分配的相同代码分配缓冲区。可能是默认进程堆。

    您只需要分配WAVEHDR结构。完成后,您有责任发布它。 mmsystem API不能为您做到这一点,最重要的是因为它们无法可靠地做到这一点。因此,您可以使用任何一个分配器,只需要确保调用相应的free方法即可。所有Windows API均以这种方式工作。我使用CoTaskMemAlloc(),但实际上没有偏好。只是如果我正在调用设计不良的代码,则使用COM堆的可能性会更高。

    在互操作方案中,永远不要使用sizeof()。它返回值类型的托管大小。在P/Invoke编码器根据[StructLayout]和[MarshalAs]指令转换结构类型之后,情况可能会有所不同。只有Marshal.SizeOf()才能为您保证正确的值。

    更新:VS2012中发生了很大的变化。现在,它随附的C运行时库从默认进程堆分配,而不是使用自己的堆。长期来看,这使AllocHGlobal成为成功的最可能途径。

    关于c# - Marshal.AllocHGlobal VS Marshal.AllocCoTaskMem,Marshal.SizeOf VS sizeof(),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/1887288/

    10-13 00:01