C# WinUSB Can't Call CloseHandle on Interface
Asked Answered
E

2

6

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?

Emmet answered 14/1, 2019 at 20:56 Comment(2)
Your Dispose implementation is faulty. Part of the contract for IDisposable is that it's legal to call Dispose multiple times on the same object. You need to keep track of whether WinUSB_Free has already been called and not call it again.Pt
Fair call. I've fixed that but it doesn't help to elucidate the question or solve the problem.Emmet
E
1

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.

Emmet answered 25/1, 2019 at 22:28 Comment(0)
O
11

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

Oestriol answered 20/1, 2019 at 13:10 Comment(15)
I've tried switching to IntPtr and it made no difference. What's the benefit of overriding SafeHandle?Emmet
From what I've read here, I've tried all the things you've suggested. I guess a simple question: am I supposed to call CloseHandle when I am finished with an interface? The documentation says I need to call WinUsb_Free but I'm not seeing even C++ examples where CloseHandle is being called. I see people calling it to close the handle for the device, but not the interface. Perhaps I am just barking up the wrong tree?Emmet
Hmm, that is not possible, after you made the changes there should be no CloseHandle() call left in the entire solution. So you can't possibly see the same exception stack trace. Search to make sure. Only DeviceHandle needs to be closed, but the SafeFileHandle.Dispose() call takes care of that.Oestriol
I'm not sure I follow. Yes, the device handle is getting released by the dispose method. What are you saying is not possible?Emmet
Can you point me to a C++ sample where CloseHandle is being called on the interface handle as opposed to the device handle?Emmet
I have edited the post to show that IntPtr does't solve the problem. I know that other people have said that it would be an issue. You can see the change in the CloseHandle2 branch. github.com/MelbourneDeveloper/Device.Net/blob/CloseHandle2/src/…Emmet
Quote: Delete the CloseHandle() call in its Dispose() method. You didn't delete it.Oestriol
The whole question is about how to call CloseHandle. Why would I delete it? I know that it works if I delete the call. In my main branch, that's what I've done.Emmet
I could repeat again that you must call WinUsb_Free() and not CloseHandle, but that's just adding up to a remarkable failure to communicate. I suspect you did not write this code, don't understand how it works and therefore don't trust my advice, consult with whomever did to get this resolved.Oestriol
What are you talking about? I'm hanging on every word you say. I am calling WinUsb_Free. It's write there in the sample. I'm not understanding what you are asking me to do.Emmet
If you doubt that I wrote the code, then feel free to scan through the Git history.Emmet
This is the code as it stands. I am only calling WinUsb_Free, and I am not calling CloseHandle. github.com/MelbourneDeveloper/Device.Net/blob/master/src/…. But, the point of the original question is how to call CloseHandle. It could be that I am barking up the wrong tree in the first place. Is that what you are telling me? Are you telling me that I should not call CloseHandle on the interface?Emmet
It looks like my original code in master was correct, that I don't need CloseHandle, and this whole question is pointless from the beginning. learn.microsoft.com/en-us/windows-hardware/drivers/usbcon/…Emmet
@MelbourneDeveloper: Nope, your code is not better. You are still storing your WINUSB_INTERFACE_HANDLE in a .NET SafeFileHandle which will automatically pass it to CloseHandle. As Hans said, you need to remove this automatic call, removing the explicit call is not enough.Pt
I'm storing it as an IntPtr. github.com/MelbourneDeveloper/Device.Net/blob/…Emmet
E
1

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.

Emmet answered 25/1, 2019 at 22:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.