Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# WinUSB Can't Call CloseHandle on Interface

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?

like image 481
Christian Findlay Avatar asked Jan 14 '19 20:01

Christian Findlay


People also ask

Is C language easy?

C is a general-purpose language that most programmers learn before moving on to more complex languages. From Unix and Windows to Tic Tac Toe and Photoshop, several of the most commonly used applications today have been built on C. It is easy to learn because: A simple syntax with only 32 keywords.

What C is used for?

C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...

What is the full name of C?

In the real sense it has no meaning or full form. It was developed by Dennis Ritchie and Ken Thompson at AT&T bell Lab. First, they used to call it as B language then later they made some improvement into it and renamed it as C and its superscript as C++ which was invented by Dr.

What is C in C language?

What is C? C is a general-purpose programming language created by Dennis Ritchie at the Bell Laboratories in 1972. It is a very popular language, despite being old. C is strongly associated with UNIX, as it was developed to write the UNIX operating system.


1 Answers

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

like image 176
Hans Passant Avatar answered Nov 01 '22 23:11

Hans Passant