LibUsbDotNet: Error on exiting WPF application - Safe handle has been closed

2.1k views Asked by At

Working with USB device in WPF application, I can successfully connect and send commands to the device. The device only receives commands, no replies so all I need is an EndpointWriter to communicate with it.

MyUsbFinder = new UsbDeviceFinder(vid, pid);
MyUsbDevice = UsbDevice.OpenUsbDevice(MyUsbFinder);
wholeUsbDevice = MyUsbDevice as IUsbDevice;
if (!ReferenceEquals(wholeUsbDevice, null))
{
    wholeUsbDevice.SetConfiguration(1);
    wholeUsbDevice.ClaimInterface(0);
}

writer = MyUsbDevice.OpenEndpointWriter(WriteEndpointID.Ep01);

And then the process of writing to the device:

byte[] update = { some bytes };
int bytesWritten;

if (MyUsbDevice != null)
{
     ec = writer.Write(update, 2000, out bytesWritten);
}

All works fine including closing the application unless during the work process the USB device is disconnected and then reconnected. The application successfully handles this scenario and reconnects to the device, continuing to successfully send commands to it:

public bool reconnect()
{
    //clear the info so far
    if (MyUsbDevice != null)
    {
        writer.Dispose();
        wholeUsbDevice.ReleaseInterface(0);
        wholeUsbDevice.Close();
        MyUsbDevice.Close();
        UsbDevice.Exit();
    }

    //now start over
    MyUsbFinder = new UsbDeviceFinder(vid, pid);
    MyUsbDevice = UsbDevice.OpenUsbDevice(MyUsbFinder);

... and so forth.

The problem occurs when I close the application after such disconnect/reconnect has occurred. Although the program worked successfully and communicated with the device for quite awhile, I get the following exception upon exiting:

System.ObjectDisposedException was unhandled
  Message="Safe handle has been closed"
  Source="mscorlib"
  ObjectName=""
  StackTrace:
       at System.StubHelpers.StubHelpers.SafeHandleC2NHelper(Object pThis, IntPtr pCleanupWorkList)
       at LibUsbDotNet.Internal.Kernel32.GetOverlappedResult(SafeHandle hDevice, IntPtr lpOverlapped, Int32& lpNumberOfBytesTransferred, Boolean bWait)
       at LibUsbDotNet.Internal.LibUsb.LibUsbAPI.GetOverlappedResult(SafeHandle interfaceHandle, IntPtr pOverlapped, Int32& numberOfBytesTransferred, Boolean wait)
       at LibUsbDotNet.Internal.OverlappedTransferContext.Wait(Int32& transferredCount, Boolean cancel)
       at LibUsbDotNet.Main.UsbTransfer.Wait(Int32& transferredCount)
       at LibUsbDotNet.Main.UsbTransfer.Dispose()
       at LibUsbDotNet.Main.UsbTransfer.Finalize()
  InnerException: 

I tried many different variations of Dispose and Exit on the usb device, the endpointwriter but no success so far. I'm assuming something remains open from before the disconnect so I get this error but I'm unsure what it is and how to get rid off it.

Since that happens only on closing the application, I wouldn't mind even being able to ignore the exception somehow and let the application die in piece but I'm not sure how to catch that exception as the Window_Closing event returns successfully and then the exception occurs after that so I'm unable to catch it...

Any tips appreciated, thanks!

2

There are 2 answers

0
Grhm On BEST ANSWER

I've been having similar issues with a "Safe handle has been closed" ObjectDisposedException happening as I close my WPF application.

My stacktrace was slightly different:

System.ObjectDisposedException was unhandled  
  HResult=-2146232798  
  Message=Safe handle has been closed  
  Source=mscorlib  
  ObjectName=""  
  StackTrace:  
       at System.Threading.WaitHandle.WaitOneNative(SafeHandle waitableSafeHandle, UInt32 millisecondsTimeout, Boolean hasThreadAffinity, Boolean exitContext)  
       at System.Threading.WaitHandle.InternalWaitOne(SafeHandle waitableSafeHandle, Int64 millisecondsTimeout, Boolean hasThreadAffinity, Boolean exitContext)  
       at System.Threading.WaitHandle.WaitOne(Int32 millisecondsTimeout, Boolean exitContext)  
       at LibUsbDotNet.Main.UsbTransfer.get_IsCancelled()  
       at LibUsbDotNet.Main.UsbTransfer.Dispose()  
       at LibUsbDotNet.Main.UsbTransfer.Finalize()  
  InnerException:

I however, have managed to fix it by adding the following code to my transmit and receive functions..

using (UsbEndpointWriter writer = device.OpenEndpointWriter(WriteEndpoint))
{
    UsbTransfer usbTransfer = null;

    try
    {
        // Code to do transfer here .. not shown as not pertinent here..
    }
    finally
    {
        if (usbTransfer != null)
        {
            // **** Start of code added to fix ObjectDisposedException
            if (!usbTransfer.IsCancelled || !usbTransfer.IsCompleted)
            {
                usbTransfer.Cancel();
            }
            // **** End of code added to fix ObjectDisposedException
            usbTransfer.Dispose();
        }
    }
}

Hopefully, this snippet might help anyone else who finds this question whilst trying to fix their issue.

1
Alex Klaus On

I received a similar exception in a Windows service. My call stack was the following:

Exception Info: System.ObjectDisposedException
Stack:
   at System.Runtime.InteropServices.SafeHandle.DangerousAddRef(Boolean ByRef)
   at System.StubHelpers.StubHelpers.SafeHandleAddRef(System.Runtime.InteropServices.SafeHandle, Boolean ByRef)
   at LibUsbDotNet.Internal.Kernel32.GetOverlappedResult(System.Runtime.InteropServices.SafeHandle, IntPtr, Int32 ByRef, Boolean)
   at LibUsbDotNet.Internal.LibUsb.LibUsbAPI.GetOverlappedResult(System.Runtime.InteropServices.SafeHandle, IntPtr, Int32 ByRef, Boolean)
   at LibUsbDotNet.Internal.OverlappedTransferContext.Wait(Int32 ByRef, Boolean)
   at LibUsbDotNet.Main.UsbTransfer.Dispose()
   at LibUsbDotNet.Main.UsbTransfer.Finalize()

To fix the issue I fixed LibUsbDotNet.Internal.LibUsb.LibUsbAPI.GetOverlappedResult() method by adding a check of interfaceHandle.IsClosed. Now the method looks like:

public override bool GetOverlappedResult(SafeHandle interfaceHandle, IntPtr pOverlapped, out int numberOfBytesTransferred, bool wait)
{
    // To prevent ObjectDisposedException check if SafeHandle's been closed
    if (!interfaceHandle.IsClosed)
        return Kernel32.GetOverlappedResult(interfaceHandle, pOverlapped, out numberOfBytesTransferred, wait);
    else
    {
        numberOfBytesTransferred = 0;
        return true;
    }
}

Hope it helps.