C# WinUSB Can't Call CloseHandle on Interface

2019-04-28 18:48发布

I am trying to release a handle to the USB interface with CloseHandle. The exception I get is:

System.Runtime.InteropServices.SEHException (0x80004005): External component has thrown an exception. at Device.Net.APICalls.CloseHandle(SafeFileHandle hObject) at Usb.Net.Windows.UsbInterface.Dispose() in C:\GitRepos\Device.Net\src\Usb.Net\Windows\UsbInterface.cs:line 23
at Usb.Net.Windows.WindowsUsbDevice.Dispose() in C:\GitRepos\Device.Net\src\Usb.Net\Windows\WindowsUsbDevice.cs:line 131

I am trying to do this in the Dispose method of my class.

Edit Background: The reason I am trying to do this is that my code is crashing the second time I run it. According to this article, I must call CloseHandle on the handle to the device that I created with CreateFile. This is done in my code anyway because the device's handle is getting disposed because it is a SafeFileHandle. But, someone told me that I need to call CloseHandle on the handle for the interface. I don't know whether this is true or not. I am attempting to do this to rule out the possibility that not calling CloseHandle is the reason for the bug. Based on other comments and research, I now believe that this was a mistake and calling WinUsb_Free should suffice. Is this correct?

Hans Passant's answer below is telling me to delete the call to CloseHandle, but as I've pointed out in the comments, the original code (in master) never called CloseHandle in the first place. Of course removing the call will work, but this isn't the question. The question as below is: What is the process for releasing a USB interface with the WinUSB API?. Is it simply to call WinUsb_Free? That's what all the information I have is leading me to believe.

This is the original dispose method from before I asked this question. It does not have the call to CloseHandle.

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

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

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

From 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 Call Definition (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);

I have also tried simply disposing of the handle because it is a SafeFileHandle, but the Dispose method of SafeFileHandle gives me the same error message. That suggests to me that SafeFileHandle's Dispose method is probably calling CloseHandle as well, and the same problem is occurring.

Other people have mentioned I should use IntPtr instead of SafeFileHandle. So, I have tried using IntPtr on the interface for CloseHandle, but the problem is the same: enter image description here

Here is the definition of WinUsb_Initialize

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

What is the process for releasing a USB interface with the WinUSB API? Is there something I need to do differently in C#? Why would this error come up?

2条回答
萌系小妹纸
2楼-- · 2019-04-28 19:16

I think the answer to this question is that it is not necessary to call CloseHandle on the USB Interface. As per the Dispose method at the top of this page, calling WinUsb_Free should suffice to free up the interface. CloseHandle only needs to be called to release the handle to the device that was created by CreateFile.

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

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

This article is pretty clear about it.

  • CloseHandle to release the handle that was created by CreateFile, as described in the step 1.
  • WinUsb_Free to release the WinUSB interface handle for the device, which is returned by WinUsb_Initialize.

Hans Passant also recommended:

Delete the CloseHandle() call in its Dispose() method

Also, from Hans Passant:

The type of the 2nd argument is incorrect, the function does not return a kernel32 handle so wrapping it in SafeFileHandle is not correct. This is an opaque handle, a WINUSB_INTERFACE_HANDLE in the native api declaration, typically a pointer under the hood. There is only one correct way to close it, you must call WinUsb_Free().

This doesn't directly deal with the question I was asking but it's a fair point. The reason I couldn't call Dispose() on the handle returned from WinUsb_Initialize as Hans points out is that doing so would call CloseHandle under the hood, and the 2nd argument returned by WinUsb_Initialize is not a kernel32 handle so CloseHandle() just won't work no matter what. This just leads to the point that there seems to be no indication whatsoever that it is necessary to call CloseHandle on the interface. So, I believe the the problem that I am having (separate issue) has nothing to do with not calling CloseHandle. It appears to be a problem with the firmware itself, and the manufacturer seems to have confirmed this. More details to come.

Note: If I'm wrong, please tell me why I'm wrong and point me to an example of where CloseHandle is used to close the handle on a USB interface.

查看更多
Viruses.
3楼-- · 2019-04-28 19:20

CloseHandle() fails when the handle is not a proper kernel32 handle or the handle is already closed. Digging through the github source code, I found out where that problem started:

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

Edited to fit and make the problem more visible. The type of the 2nd argument is incorrect, the function does not return a kernel32 handle so wrapping it in SafeFileHandle is not correct. This is an opaque handle, a WINUSB_INTERFACE_HANDLE in the native api declaration, typically a pointer under the hood. There is only one correct way to close it, you must call WinUsb_Free(). The code does so, but also calling CloseHandle is not correct and doomed to fail. The CloseHandle() call provided by SafeFileHandle will likewise fail, you probably didn't get that far yet.

Change the argument type to IntPtr. That requires several other code changes, primarily in the UsbInterface class. Likewise change its Handle property type to IntPtr. Delete the CloseHandle() call in its Dispose() method. Writing your own SafeHandle-derived class to wrap it is another way, you'd then override ReleaseHandle() to call WinUsb_Free().

查看更多
登录 后发表回答