Sometimes you just have no choice. You have resort to some hack in order to make your code work. I recently had to write a GUI client to provide some specialist admin tasks on DataSynapse GridServer v2.6. For various reasons this had to be a C# .NET GUI and DataSynapse thoughtfully provide a .NET API which made the coding tasks pretty straightforward. Everything worked beautifully except when the GUI closes and the App Domain doesn’t unload. Consequently the .exe and vshost.exe just hang around like ghostly processes.

Some investigation revealed that whenever any of the DataSynapse admin .NET API methods are called there’s a sleep, wait or join that never exits.

[In a sleep, wait, or join]
mscorlib.dll!System.Threading.Monitor.Wait(object obj, int millisecondsTimeout, bool exitContext) + 0x14 bytes

It turns out this is a known bug that I’m sure will be fixed at some stage. However, that didn’t really help me. Despite some failed attempts to terminate the thread, I resorted to some rug pulling. The code now uses my old Suppuku class to make sure everything terminates.

static void Main()
{
      Application.SetCompatibleTextRenderingDefault(false);
      new ShellApplication().Run();
      Suppuku.Sword.Commit(0);
}

And here’s the brutal implementation.

namespace Suppuku
{
      public class Sword
      {
            [DllImport("Kernel32.dll")]
            private static extern bool TerminateProcess(int handle, int exitCode);
            public static void Commit(int exitCode)
            {
                  TerminateProcess(-1, exitCode);
            }
      }
}

Sometimes needs must and Win32 comes to the rescue.  Any other solutions gratefully excepted.

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.

  1. Read the Platform Invoke Tutorial
  2. 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.
  3. 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.
  4. 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.
  5. Check the size of struct parameters match that required by the unmanaged function.
  6. Check the types and size of your struct members.
  7. 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.
  8. 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.
  9. 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);
  10. Read the Platform Invoke Tutorial one more time.