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>
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.