我有以下结构:
[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))
传递给内存分配方法吗?有什么区别?请注意,必须固定分配的内存。
答案 0 :(得分:41)
Windows程序总是至少有两个堆,其中分配了非托管内存。首先是Windows在需要代表程序分配内存时使用的默认进程堆。第二个是COM基础结构用于分配的堆。 .NET P / Invoke marshaller假定这个堆被任何非托管代码使用,其代码需要取消分配内存。
AllocHGlobal从进程堆分配,AllocCoTaskMem从COM堆分配。
每当编写非托管互操作代码时,应始终避免分配非托管内存的代码与释放它的代码不同的情况。使用错误的解除分配器的可能性很大。对于使用C / C ++程序进行交互的任何代码尤其如此。这些程序有自己的分配器,它使用自己的堆,由CRT在启动时创建。在其他代码中取消分配这样的内存是不可能的,你无法可靠地获得堆处理。这是P / Invoke问题的一个非常常见的原因,特别是因为XP和更早版本中的HeapFree()函数无声地忽略了释放未在正确的堆中分配的内存的请求(泄漏分配的内存)但Vista和Win7崩溃了程序有例外。
在您的情况下无需担心,您使用的mmsystem API函数是干净的。它们旨在确保分配也释放的相同代码。这是你必须调用waveInPrepareHeader()的一个原因,它使用相同的代码分配缓冲区,最终解除分配它们。可能与默认进程堆。
您只需要分配WAVEHDR结构。当你完成它时,你有责任释放它。 mmsystem API不适合你,最重要的是因为它们无法可靠地执行此操作。因此,您可以使用任一分配器,您只需要确保调用相应的自由方法。所有Windows API都以这种方式工作。我使用CoTaskMemAlloc(),但确实没有偏好。只是如果我调用设计错误的代码,使用COM堆稍微有点可能。
您绝不应在互操作方案中使用sizeof()。它返回值类型的托管大小。在P / Invoke marshaller根据[StructLayout]和[MarshalAs]指令转换结构类型之后,这可能不一样。只有Marshal.SizeOf()能为您提供有保证的正确值。
更新:VS2012发生了很大变化。它包含的C运行时库现在从默认进程堆分配,而不是使用自己的堆。从长远来看,这使得AllocHGlobal成为最有可能获得成功的途径。
答案 1 :(得分:2)
1)Marshal.AllocHGlobal肯定会工作。根据Marshal.AllocCoTaskMem的文档,Marshal.AllocCoTaskMem也应该工作。
2)使用Marshal.SizeOf(typeof(WAVEHDR))。 Although you can use the Marshal.SizeOf method, the value returned by this method is not always the same as the value returned by sizeof. Marshal.SizeOf returns the size after the type has been marshaled, whereas sizeof returns the size as it has been allocated by the common language runtime, including any padding.
答案 2 :(得分:1)
2)据我所知,sizeof
只能用于编译时预定义大小的类型。
所以请使用Marshal.SizeOf(typeof(WAVEHDR))
。