没有 SafeHandle 的 DllImport 导致 MissingMethodException

DllImport with out SafeHandle results in MissingMethodException

我写了一个包装器 class 来访问 QuickUsb 的 c# 中的非托管库。有关完整的实施,请参阅此 gist.

本题主要关注点如下:

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;
        }
    }
}

似乎将 handle 编组为 SafeQuickUsbHandle 时出现问题,因为在调用 Open() 时此代码会抛出 MissingMethodException。然而,以下修改不会引发此类异常:

        [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);
        }

所以我想知道我是否遗漏了我的 SafeQuickUsbHandle 实现的某些方面以允许 c# 正确编组和处理句柄。

注意在dll中,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>

您必须为派生自 SafeHandle, especially when you it with p/invoke, as defined here: SafeHandle

的 class 提供 public 无参数构造函数

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 无论如何都会神奇地设置值。它也在 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.

由于 SafeHandleZeroOrMinusOneIsInvalid 没有定义 public 无参数构造函数,您必须定义它。