wiki:Windows/DotNetPlatformInvokeRefcountBug

BUG? P/Invoke DLL refcount not decremented before destroying managed application?

(Posted to  MSDN .net framework CLR forum)

I'm working with Visual Studio C# 2008 Express Edition with a target of .net Framework 2.0. I have the latest Win32 SDK (v3.1) installed and configured for use in Visual Studio.

The application is a simple Form that presents a few controls that display values retrieved from calls to an unmanged DLL using Platform Invoke.

I was experiencing a consistent problem that (whether running Debug or Release) Windows Vista would report an Invalid Access (0xc0000005) Exception in an unmanaged DLL when the application was closed. No JIT debugging was available.

After hours of searching and reading up on P/Invoke I came across a fragment of a blog posting that talked about manually forcing a library to unload by using P/Invoke to call LoadLibrary() and FreeLibrary().

In order to force the library to be unloaded it is necessary to call FreeLibrary() twice - to account for the DLLImport refcount as well as the manual LoadLibrary().

After implementing this simple code the application I am working on no longer suffers the Invalid Access exceptions.

This seems like the managed code memory is being disposed of before the unmanaged DLL has its refcount decremented, and therefore any access by the DLL to memory is going to cause the exception.

I imagine this isn't seen much for popular system DLLs since their refcount is probably going to keep them around most of the time. For DLLs that are rarely used, however, this looks to be a particular issue.

Example code to solve issue:

public partial MyClass : Form
{
  // workaround for Platform Invoke issue
  [DllImport("kernel32.dll")]
  public static extern IntPtr LoadLibrary(string lpFileName);
  [DllImport("kernel32.dll")]
  public static extern IntPtr FreeLibrary(IntPtr library);
  private String DllPath = "C:\\Program Files\\Common Files\\Sony Shared\\Sony Utilities\\SnyUtils.dll";
  private IntPtr dll;
 
  // DLL required by application
  [DllImport("C:\\Program Files\\Common Files\\Sony Shared\\Sony Utilities\\SnyUtils.dll")]
  public static extern IntPtr SuOpen([In, Out] ref int arg1, [In, Out] ref int arg2);
  [DllImport("C:\\Program Files\\Common Files\\Sony Shared\\Sony Utilities\\SnyUtils.dll")]
  public static extern int SuClose(IntPtr handle);
 
  private int result = -1;
  private IntPtr handle;
 
  public MyClass()
  {
    // load DLL explicitly (increases refcount)
    dll = LoadLibrary(DllPath);
 
    // use the DLL
    handle = SuOpen(ref arg1,ref arg2);
 
    result = SuClose(handle);
    handle = IntPtr.Zero;
 
    // unload DLL explicitly twice (refcount was incremented by DllImport)
    handle = FreeLibrary(dll);
    handle = FreeLibrary(dll);
  }
}

Removing that 2nd FreeLibrary() call will provoke the Invalid Access exception.

I'm not sure if this is specific to the DLL I'm working with or a more general issue with P/Invoke.