There’s a new marshal in town
February 2, 2007
Unless you’ve been sitting or living under a very large stone over the past couple of years, you’ll know that Microsoft would have us believe Win32 is legacy. So why have they put so much effort into the Interop services? Trust me; it will be around for some time. I take my hat off to the guys who drew the short straw and did it – I bet it was a mission and the end result pretty much works.
However, to the GUI developer who’s experienced the infuriating little dialog box which says something like:
Managed Debugging Assistant ‘PInvokeStackImbalance’ has detected a problem in ‘C:\dev\ SendToCS\bin\Debug\SendToCS.vshost.exe’.
Additional Information: A call to PInvoke function ‘SendToCS!SendFileTo.MAPI::MAPISendMail’ has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.
when tracking down problems in calls from managed to unmanaged code, here’s my pain induced, top ten tips to sorting out problems with interop marshalling code.
- Read the Platform Invoke Tutorial
- Check for a mismatch between your unmanaged function signature and the declared [DllImport] C# function declaration and double check the DLL name it’s implemented in.
- Use ref’s so the CLR will pass your structs as a reference to the unmanaged function so it can modify your object, not a orphan stack copy. You must use ref in the function declaration as well as the call.
- Check the parameter types – particulary strings vs StringBuilder and the use of ref for string values returned in parameters. Remember string is immutable, although .NET will marshal both to a LPTSTR.
- Check the size of struct parameters match that required by the unmanaged function.
- Check the types and size of your struct members.
- Remember to use [StructLayout(LayoutKind .Sequential)] attributes on structs or classes you reference in parameter pointers. The CLR controls the physical layout of memory, so if the class needs to be arranged in an expected way use StructLayout to preserve the layout and .NET will not mess with it.
- Although it’s platform specific, IntPtr is a useful, thread safe structure to represent a pointer or handle – HWNDs are probably the single most common parameter.
- The default marshalling for structs is an LPSTRUCT, so there’s nothing to do. If you make it a class you’ll need to marshal it like this [MarshalAs(UnmanagedType.LPStruct)] MapiMessage pMsg);
- Read the Platform Invoke Tutorial one more time.