Marshal.AllocHGlobal VS Marshal.AllocCoTaskMem,Marshal.SizeOf VS sizeof()

我有以下结构:

[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 } 

我需要分配非托管内存来存储上面结构的实例。 指向此结构的指针将传递给waveOut win32 api函数(waveOutPrepareHeader,waveOutWrite,waveOutUnprepareHeader)。

  1. 我应该使用Marshal.AllocHGlobal()还是Marshal.AllocCoTaskMem() ? 有什么不同?
  2. 我应该将sizeof(WAVEHDR)Marshal.SizeOf(typeof(WAVEHDR))传递给内存分配方法吗? 有什么不同?

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

Windows程序始终至少有两个堆,其中分配了非托管内存。 首先是Windows在需要代表程序分配内存时使用的默认进程堆。 第二个是COM基础结构用于分配的堆。 .NET P / Invoke marshaller假定这个堆被任何非托管代码使用,其代码需要取消分配内存。

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

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

在您的情况下无需担心这一点,您使用的mmsystem API函数是干净的。 它们的目的是确保分配deallocates的代码相同。 这是你必须调用waveInPrepareHeader()的一个原因,它使用相同的代码分配缓冲区,最终解除分配它们。 可能与默认进程堆。

您只需要分配WAVEHDR结构。 当你完成它时,你有责任释放它。 mmsystem API不适合你,最重要的是因为它们无法可靠地执行此操作。 因此,您可以使用任一分配器,您只需要确保调用相应的自由方法。 所有Windows API都以这种方式工作。 我使用CoTaskMemAlloc(),但确实没有偏好。 只是如果我调用设计糟糕的代码,使用COM堆稍微有点可能。

你绝不应该在互操作场景中使用sizeof()。 它返回值类型的托管大小。 在P / Invoke marshaller根据[StructLayout]和[MarshalAs]指令转换结构类型之后,这可能不一样。 只有Marshal.SizeOf()为您提供有保证的正确值。


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

1)Marshal.AllocHGlobal肯定会工作。 根据Marshal.AllocCoTaskMem的文档,Marshal.AllocCoTaskMem也应该工作。

2)使用Marshal.SizeOf(typeof(WAVEHDR))。 虽然您可以使用Marshal.SizeOf方法,但此方法返回的值并不总是与sizeof返回的值相同。 Marshal.SizeOf在封送类型后返回大小,而sizeof返回公共语言运行库分配的大小,包括任何填充。

2)据我所知, sizeof只能用于编译时具有预定义大小的类型。

所以使用Marshal.SizeOf(typeof(WAVEHDR))