Search code examples
c#pinvoke

DllImport with out SafeHandle results in MissingMethodException


I wrote a wrapper class to access an unmanaged lib in c# for QuickUsb. For the full fledged implementation see this gist.

The main points of interest to this question are the following parts:

public class QuickUsbPort
{
    private class SafeQuickUsbHandle : SafeHandleZeroOrMinusOneIsInvalid
    {
        [DllImport("QuickUsb.dll", CharSet = CharSet.Ansi)] static extern
            int QuickUsbClose(IntPtr handle);

        public SafeQuickUsbHandle(IntPtr handle) : base(true)
        {
            SetHandle(handle);
        }

        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        protected override bool ReleaseHandle()
        {
            return QuickUsbClose(handle) != 0;
        }
    }

    private static class NativeLib
    {
        [DllImport("QuickUsb.dll", CharSet = CharSet.Ansi)] static extern
            int QuickUsbOpen(out SafeQuickUsbHandle handle, string deviceName);

        public static SafeQuickUsbHandle Open(string deviceName)
        {
            if (QuickUsbOpen(out SafeQuickUsbHandle handle, deviceName) == 0)
            {
                throw new QuickUsbException("Open", new List<Tuple<string, string>>
                {
                    new Tuple<string, string>("deviceName", deviceName),
                });
            }
            return handle;
        }
    }
}

It seems there is a problem marshaling handle as a SafeQuickUsbHandle as upon calling Open() this code throws a MissingMethodException. However the following modification throws no such exception:

        [DllImport("QuickUsb.dll", CharSet = CharSet.Ansi)] static extern
            int QuickUsbOpen(out IntPtr handle, string deviceName);

        public static SafeQuickUsbHandle Open(string deviceName)
        {
            if (QuickUsbOpen(out IntPtr handle, deviceName) == 0)
            {
                throw new QuickUsbException("Open", new List<Tuple<string, string>>
                {
                    new Tuple<string, string>("deviceName", deviceName),
                });
            }
            return new SafeQuickUsbHandle(handle);
        }

So I'm wondering if I'm missing some aspect of my SafeQuickUsbHandle implementation to allow c# to properly marshal and dispose of the handle.

Note that in the dll, handle is pointer to a handle:

/// <param name="handle">
/// A PQHANDLE that points to a QHANDLE in which to place the new device ID.
/// If successful, hDevice will contain the new QHANDLE</param>

Solution

  • You must provide a public parameterless constructor to a class that derives from SafeHandle, especially when you it with p/invoke, as defined here: SafeHandle

    Your subclass of SafeHandle is only required to provide three methods

    .ctor() – A default constructor that initializes the SafeHandle. This method is used by P/Invoke when it returns a SafeHandle to your process

    bool IsInvalid { get; } – a property to determine if the current value of the handle is valid or not

    bool ReleaseHandle() – clean up the contained resource

    p/invoke will set the value magically anyway. It's also in the official documentation:

    When you inherit from SafeHandle, you must override the following members: IsInvalid and ReleaseHandle. You should also provide a default constructor that calls the base constructor with a value that represent an invalid handle value, and a Boolean value indicating whether the native handle is owned by the SafeHandle and consequently should be freed when that SafeHandle has been disposed.

    Since SafeHandleZeroOrMinusOneIsInvalid doesn't define a public parameterless constructor, you must do it.