Marshal.AllocHGlobal VS Marshal.AllocCoTaskMem, Marshal.SizeOf VS sizeof()
Asked Answered
B

3

35

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).

  1. Should I use Marshal.AllocHGlobal() or Marshal.AllocCoTaskMem()? What is the difference?
  2. Should I pass sizeof(WAVEHDR) or Marshal.SizeOf(typeof(WAVEHDR)) to the memory allocation method? What is the difference?

NOTE that the allocated memory must be pinned.

Butlery answered 11/12, 2009 at 10:52 Comment(0)
O
53

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.

Obtuse answered 11/12, 2009 at 12:28 Comment(3)
Are there any performance differences between the two allocate functions?Butlery
AllocCoTaskMem is more performant. AllocHGlobal calls LocalAlloc, which has the following note: "The local functions have greater overhead and provide fewer features than other memory management functions." See msdn.microsoft.com/en-us/library/windows/desktop/…Leannaleanne
That is unlikely to be accurate anymore, starting with Win8. LocalAlloc() and CoTaskMemAlloc() now both allocate from the process heap. The probable reason for the CRT change. I never saw a documented good reason for this drastic change, but assume it has something to do with WinRT (aka Metra, aka Store, aka UWP). Biggest issue is that you can't be sure anymore that your program is memory-clean on Win7, testing required.Obtuse
S
4

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)).

Sclerous answered 11/12, 2009 at 11:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.