.NET Interop - Freeing unmanaged memory
OK. Imagine you need to call an unmanaged function. Now imagine this function returns you a pointer to a block of unmanaged memory that it's allocated. The runtime will clear this up for you right? Wrong. Well, wrong a lot of the time anyway.
The runtime always attempts to free unmanaged memory using the COM method - CoTaskMemAlloc. So let's say we have a function that returns a pointer to unmanaged memory that we're going to marshal directly as a .NET type - System.String. Once the System.String object has been created (the runtime uses a copy of the unmanaged data to build this), the runtime will attempt to clean up the unmanaged data with a call to CoTaskMemFree. This is all well and good providing the memory was allocated with CoTaskMemAlloc. If it wasn't though, you'll end up with a memory leak.
Luckily, the solution is simple. The only tricky item is finding out which method the function used to allocated the memory. If you find out the C runtime was used, you can pass the data back as an IntPtr and then pass this IntPtr to an unmanaged function you've written to be cleaned up properly. (You could of course re write the original unmanaged function to use CoTaskMemAlloc, but I'm assuming in most cases this isn't possible). Let's take a look at a very simple example.
Our unmanaged function allocates memory for a Unicode string and returns a pointer to this memory to us:
extern
"C" __declspec(dllexport) wchar_t* GetString(const wchar_t* inString);
wchar_t
* GetString(const wchar_t* inString)
{
wchar_t* retValue = new wchar_t[wcslen(inString) + 1];
wcscpy(retValue, inString);
return retValue;
}
Now, when we write our DllImport statement to expose this function to our managed code, we might be tempted to simply cast the return value directly as System.String. However, this will leave us unable to clean up the unmanaged memory allocated above. Instead, we need to cast it as an IntPtr, which we can then pass to an unmanaged function written specifically to clean memory for us.
[DllImport("YourDLL", CharSet=CharSet.Unicode)]
public static extern IntPtr GetString(string inString);
Our unmanaged helper function for freeing memory might look like this:
extern "C" __declspec(dllexport) void FreeMemory(void* memToFree);
Leaving us with the small job of adding a DllImport statement for it (I'm sure you can manage this part yourselves - email me if you can't!) and then using it in code:
IntPtr pUnmanagedMemory = NativeMethods.GetString("123456789");
string theString = Marshal.PtrToStringUni(pUnmanagedMemory);
Console.WriteLine(theString);
NativeMethods.FreeMemory(pUnmanagedMemory);
Note the Marshal.PtrToStringUni method. There are a variety of these PtrTo* methods for just such occasions as this. Of course (as in all my examples) you'd probably want to wrap the interop calls in try/catch blocks and place your cleanup code in the finally.
Enjoy!