c# - Marshal.AllocHGlobal VS Marshal.AllocCoTaskMem,Marshal.SizeOf VS sizeof()

标签 c# .net winapi memory-management unmanaged

我有以下结构:

[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 编码器假设此堆被任何非托管代码使用,其函数签名需要取消分配内存。

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 编码器根据 [StructLayout] 和 [MarshalAs] 指令转换结构类型后,这可能会有所不同。只有 Marshal.SizeOf() 可以为您提供有保证的正确值。


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

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

相关文章:

c# - 什么是NullReferenceException,如何解决?

c# - .Net 开发与 SQLServer 的配对在 ORM 方面有什么优势?

windows - 发现窗口和按钮等对象的 HWND 的程序

c - 如何使用IPictureDisp::Invoke正确调用Render成员

c# - 如何在遍历 Treeview 时设置位置

c# - 如何防止在编辑 DataGridViewTextBoxColumn 并按 Enter 键后转到下一行?

c# - 从 DataGridView 中的枚举创建下拉列表选项

c++ - 为什么我不能在 VS.NET 调试器中单步执行剪贴板代码?

c# - 运行 Windows Phone 8 应用程序时出现 System.IO.FileNotFoundException 错误

c# - Firebird 在 .NET 中发送所有事件