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)。
- 我应该使用
Marshal.AllocHGlobal()
还是Marshal.AllocCoTaskMem()
?有什么区别? - 我应该将
sizeof(WAVEHDR)
或Marshal.SizeOf(typeof(WAVEHDR))
传递给内存分配方法吗?有什么区别?
请注意,分配的内存必须固定。
I have the following struct:
[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
}
I need to allocate unmanaged memory to store an instance of above struct. A pointer to this struct will be passed to waveOut win32 api functions (waveOutPrepareHeader, waveOutWrite, waveOutUnprepareHeader).
- Should I use
Marshal.AllocHGlobal()
orMarshal.AllocCoTaskMem()
? What is the difference? - Should I pass
sizeof(WAVEHDR)
orMarshal.SizeOf(typeof(WAVEHDR))
to the memory allocation method? What is the difference?
NOTE that the allocated memory must be pinned.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
Windows 程序始终至少有两个堆,其中分配了非托管内存。第一个是默认的进程堆,Windows 在需要代表程序分配内存时使用它。第二个是 COM 基础结构用来分配的堆。 .NET P/Invoke 编组器假定该堆已被其函数签名需要取消分配内存的任何非托管代码使用。
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 成为最有可能成功的途径。
A Windows program always has at least two heaps in which unmanaged memory is allocated. First is the default process heap, used by Windows when it needs to allocate memory on behalf of the program. The second is a heap used by the COM infrastructure to allocate. The .NET P/Invoke marshaller assumes this heap was used by any unmanaged code whose function signature requires de-allocating memory.
AllocHGlobal allocates from the process heap, AllocCoTaskMem allocates from the COM heap.
Whenever you write unmanaged interop code, you should always avoid a situation where code that allocates unmanaged memory is not the same as the code that frees it. There would be a good chance that the wrong de-allocator is used. This is especially true for any code that interops with a C/C++ program. Such programs have their own allocator that uses its own heap, created by the CRT at startup. De-allocating such memory in other code is impossible, you can't reliably get the heap handle. This is a very common source of P/Invoke trouble, especially because the HeapFree() function in XP and earlier silently ignore requests to free memory that wasn't allocated in the right heap (leaking the allocated memory) but Vista and Win7 crash the program with an exception.
No need to worry about this in your case, the mmsystem API functions you are using are clean. They were designed to ensure the same code that allocates also deallocates. This is one reason you have to call waveInPrepareHeader(), it allocates buffers with the same code that ultimately deallocates them. Probably with the default process heap.
You only need to allocate the WAVEHDR structure. And you are responsible for releasing it when you're done with it. The mmsystem APIs don't do it for you, most of all because they cannot do so reliably. Accordingly, you can use either allocator, you just need to make sure to call the corresponding free method. All Windows APIs work this way. I use CoTaskMemAlloc() but there really isn't a preference. Just that if I'm calling badly designed code, it is slightly likelier to use the COM heap.
You should never use sizeof() in an interop scenario. It returns the managed size of value type. That might not be the same after the P/Invoke marshaller has translated a structure type according to the [StructLayout] and [MarshalAs] directives. Only Marshal.SizeOf() gives you a guaranteed correct value.
UPDATE: there was a big change in VS2012. The C runtime library included with it now allocates from the default process heap instead of using its own heap. Long term, that makes AllocHGlobal the most likely avenue for success.
1)Marshal.AllocHGlobal 肯定会工作。根据 Marshal.AllocCoTaskMem 的文档,Marshal.AllocCoTaskMem 也应该可以工作。
2) 使用 Marshal.SizeOf(typeof(WAVEHDR))。 虽然可以使用 Marshal.SizeOf 方法,但该方法返回的值不是始终与 sizeof 返回的值相同。 Marshal.SizeOf 返回类型被封送后的大小,而 sizeof 返回公共语言运行时分配的大小,包括任何填充。
1) Marshal.AllocHGlobal will work for sure. Based on the docs for Marshal.AllocCoTaskMem, Marshal.AllocCoTaskMem should work too.
2) Use 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) 据我所知,
sizeof
只能与在编译时具有预定义大小的类型一起使用。因此,请使用Marshal.SizeOf(typeof(WAVEHDR))。
2) As far as I know the
sizeof
can only be used with types that have a predefined size at the compile-time.So use
Marshal.SizeOf(typeof(WAVEHDR))
.