C#WinUSB无法在接口上调用CloseHandle

时间:2019-01-14 20:56:43

标签: c# usb winusb safefilehandle

我正在尝试使用CloseHandle释放USB接口的句柄。我得到的例外是:

  

System.Runtime.InteropServices.SEHException(0x80004005):外部   组件引发了异常。在   Device.Net.APICalls.CloseHandle(SafeFileHandle hObject)位于   Usb.Net.Windows.UsbInterface.Dispose()在   C:\ GitRepos \ Device.Net \ src \ Usb.Net \ Windows \ UsbInterface.cs:第23行
  在Usb.Net.Windows.WindowsUsbDevice.Dispose()中   C:\ GitRepos \ Device.Net \ src \ Usb.Net \ Windows \ WindowsUsbDevice.cs:line   131

我正在尝试在班级的Dispose方法中执行此操作。

编辑背景: 我尝试执行此操作的原因是,我的代码第二次运行时崩溃。根据此article,我必须在用CreateFile创建的设备的句柄上调用CloseHandle。无论如何,这是在我的代码中完成的,因为设备的句柄因为是SafeFileHandle而被处置。但是,有人告诉我,我需要在该接口的句柄上调用CloseHandle。我不知道这是不是真的。我正在尝试执行此操作,以排除不调用CloseHandle是该错误的原因的可能性。根据其他评论和研究,我现在认为这是一个错误,调用WinUsb_Free应该足够了。这是正确的吗?

Hans Passant在下面的回答是告诉我删除对CloseHandle的调用,但是正如我在注释中所指出的那样,原始代码(在master中)从来没有首先调用CloseHandle。当然,删除呼叫会起作用,但这不是问题。下面的问题是:使用WinUSB API释放USB接口的过程是什么?。仅仅是调用WinUsb_Free吗?那就是我所拥有的所有信息使我相信。

这是我问这个问题之前的原始处理方法。它没有对CloseHandle的调用。

    public void Dispose()
    {
        if (_IsDisposed) return;
        _IsDisposed = true;

        var isSuccess = WinUsbApiCalls.WinUsb_Free(Handle);
        WindowsDeviceBase.HandleError(isSuccess, "Interface could not be disposed");
    }

从WindowsUsbInterface(https://github.com/MelbourneDeveloper/Device.Net/blob/9ebc122a2755dda2824c6eda961d092f2f6e83b5/src/Usb.Net/Windows/WindowsUsbDevice.cs#L122):

    public override void Dispose()
    {
        if (_IsDisposing) return;
        _IsDisposing = true;

        try
        {
            foreach (var usbInterface in _UsbInterfaces)
            {
                usbInterface.Dispose();
            }

            _UsbInterfaces.Clear();

            _DeviceHandle?.Dispose();
            _DeviceHandle = null;

            base.Dispose();
        }
        catch (Exception ex)
        {
            Logger.Log("Error disposing of device", ex, nameof(WindowsUsbDevice));
        }

        _IsDisposing = false;
    }

从UsbInterface(https://github.com/MelbourneDeveloper/Device.Net/blob/9ebc122a2755dda2824c6eda961d092f2f6e83b5/src/Usb.Net/Windows/UsbInterface.cs#L18):

    public void Dispose()
    {
        var isSuccess = WinUsbApiCalls.WinUsb_Free(Handle);
        WindowsDeviceBase.HandleError(isSuccess, "Interface could not be disposed");

        isSuccess = APICalls.CloseHandle(Handle);
        WindowsDeviceBase.HandleError(isSuccess, "Interface could not be disposed");
    }

API调用定义(https://github.com/MelbourneDeveloper/Device.Net/blob/CloseHandle/src/Device.Net/Windows/APICalls.cs):

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern bool CloseHandle(SafeFileHandle hObject);

我也尝试过简单地处理该句柄,因为它是一个SafeFileHandle,但是SafeFileHandle的Dispose方法会给我同样的错误消息。这向我暗示了SafeFileHandle的Dispose方法可能也正在调用CloseHandle,并且发生了相同的问题。

其他人提到我应该使用IntPtr而不是SafeFileHandle。因此,我尝试在CloseHandle的接口上使用IntPtr,但问题是相同的: enter image description here

这是WinUsb_Initialize

的定义
[DllImport("winusb.dll", SetLastError = true)]
public static extern bool WinUsb_Initialize(SafeFileHandle DeviceHandle, out IntPtr InterfaceHandle);

使用WinUSB API释放USB接口的过程是什么?在C#中我需要做些不同的事情吗?为什么会出现此错误?

2 个答案:

答案 0 :(得分:10)

当句柄不是适当的kernel32句柄或该句柄已关闭时,

CloseHandle()失败。深入研究github源代码,我发现了问题的出处:

    [DllImport("winusb.dll", SetLastError = true)]
    public static extern bool WinUsb_Initialize(SafeFileHandle DeviceHandle,
                                                out SafeFileHandle InterfaceHandle);

经过编辑以适合并使问题更明显。第二个参数的类型不正确,该函数未返回kernel32句柄,因此将其包装在SafeFileHandle中是不正确的。这是一个不透明的句柄,在本机api声明中为WINUSB_INTERFACE_HANDLE,通常是一个隐藏的指针。关闭它只有一种正确的方法,您必须调用WinUsb_Free()。代码可以这样做,但是调用CloseHandle是不正确的,并且注定会失败。 SafeFileHandle提供的CloseHandle()调用同样会失败,您可能还没走那么远。

将参数类型更改为IntPtr。这需要其他几处代码更改,主要是在UsbInterface类中。同样,将其Handle属性类型更改为IntPtr。在其Dispose()方法中删除CloseHandle()调用。编写自己的SafeHandle派生类来包装它是另一种方法,然后重写ReleaseHandle()来调用WinUsb_Free()。​​

答案 1 :(得分:-1)

我认为这个问题的答案是没有必要在USB接口上调用CloseHandle。按照本页顶部的Dispose方法,调用WinUsb_Free应该足以释放接口。只需调用CloseHandle即可释放由CreateFile创建的设备的句柄。

public void Dispose()
{
    if (_IsDisposed) return;
    _IsDisposed = true;

    var isSuccess = WinUsbApiCalls.WinUsb_Free(Handle);
    WindowsDeviceBase.HandleError(isSuccess, "Interface could not be disposed");
}

这个article非常清楚。

  
      
  • CloseHandle释放由CreateFile创建的句柄,如步骤1所述。
  •   
  • WinUsb_Free释放设备的WinUSB接口句柄,由WinUsb_Initialize返回。
  •   

汉斯·帕桑特还建议:

  

在其Dispose()方法中删除CloseHandle()调用

也来自Hans Passant:

  

第二个参数的类型不正确,该函数不正确   返回kernel32句柄,因此不将其包装在SafeFileHandle中   正确。这是一个不透明的句柄,它位于WINUSB_INTERFACE_HANDLE   本地api声明,通常是一个隐藏的指针。有   只有一种正确的关闭方式,您必须调用WinUsb_Free()。​​

这并不能直接解决我所问的问题,但这是很公平的。汉斯指出,我无法在WinUsb_Initialize返回的句柄上调用Dispose()的原因是,这样做会在幕后调用CloseHandle,而WinUsb_Initialize返回的第二个参数不是kernel32句柄,因此CloseHandle()会赢得胜利。无论如何都不能工作。这只会导致这样的观点:似乎没有任何迹象表明必须在接口上调用CloseHandle。因此,我相信我遇到的问题(单独的问题)与不调用CloseHandle无关。固件本身似乎有问题,制造商似乎已确认这一点。还有更多细节。

注意:如果我错了,请告诉我为什么我错了,并举一个使用CloseHandle关闭USB接口上的句柄的示例。 < / strong>