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

http://stackoverflow.com/questions/1887288/marshal-allochglobal-vs-marshal-alloccotaskmem-marshal-sizeof-vs-sizeof


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.

### System.Runtime.InteropServices.Marshal 的使用方法 `System.Runtime.InteropServices.Marshal` 是 .NET Framework 提供的一个类,主要用于在托管代码和非托管代码之间进行内存管理和数据转换。此类提供了多种静态方法来处理指针操作、结构体与对象之间的相互转换以及字符串和其他类型的封送等功能[^1]。 #### 方法概述 - **AllocHGlobal 和 FreeHGlobal**: 分配和释放未管理堆上的内存块。 ```csharp IntPtr ptr = Marshal.AllocHGlobal(10); try { // Use the allocated memory... } finally { Marshal.FreeHGlobal(ptr); } ``` - **PtrToStructure 和 StructureToPtr**: 将指向未管理内存中的结构体的数据复制到托管对象中或将托管对象的内容复制到未管理内存位置。 ```csharp [StructLayout(LayoutKind.Sequential)] struct POINT { public int X; public int Y; } // Convert unmanaged pointer to managed structure. POINT pt = (POINT)Marshal.PtrToStructure(ptr, typeof(POINT)); ``` - **StringToHGlobalAnsi/StringToHGlobalUni 和 PtrToStringAnsi/PtrToStringUni**: 实现字符串与字符数组间的转换。 ```csharp string s = "Hello"; IntPtr pnt = Marshal.StringToHGlobalAnsi(s); Console.WriteLine(Marshal.PtrToStringAnsi(pnt)); Marshal.FreeHGlobal(pnt); // Always free when done with it. ``` - **SizeOf 和 OffsetOf**: 获取给定类型实例所需的空间大小或字段偏移量。 ```csharp Type type = typeof(MyType); int size = Marshal.SizeOf(type); FieldOffset offset = Marshal.OffsetOf(typeof(MyType), "fieldName"); ``` 对于 `MarshalAsAttribute` 属性的应用可以参照如下例子: ```csharp // 应用于参数上 public void M1([MarshalAs(UnmanagedType.LPWStr)]string msg) // 应用于类内的字段 class MsgText { [MarshalAs(UnmanagedType.LPWStr)] public string msg; } // 应用于返回值 [return: MarshalAs(UnmanagedType.LPWStr)] public string GetMessage() ``` 当涉及到不同平台(如32位 vs 64位)时,需要注意的是,在C#中 `void*` 类型通常映射为 `IntPtr` 来表示指针,这使得程序可以在不同的架构下正常工作[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值