ObjectDisposedException: Safe handle has been closed
Asked Answered
S

4

7

So this is a rather small question with a big explanation. As is noted by the title I am getting an unhandled exception telling me my Safe handle has been closed. What I'll probably have to do is edit this post a few times with more and more code to help me diagnose what the problem is.

I'm using POS for .NET to make a Service Object for my RFID and MSR device. Although my devices are the same, I have 2 different Virtual COM Port chips that communicate to those devices. One by Silicon labs, the other by FTDI. I wanted to use the plug and play features with POS for .NET so I gave it both my Hardware ID's. Because it is plug and play I have the full hardware path available to me which I can then create a SafeFileHandle using a call to PInvoke and using that SafeFileHandle I create a FileStream. The FTDI chip doesn't let me talk to the devices directly like that so I have to get the friendly name of the device then use mutex to pull out the COM port then create a SerialPort instance. That step works fine and great. As a FYI I have tried to use the Friendly name of both chips to get the COM port and the Silicon Labs one (for some strange reason) doesn't get listed using SetupAPI.GetDeviceDetails using the Ports GUID. I'm not sure on that one since in Device Manager the Silicon labs Device Class Guid is the Ports GUID.

Well since both the SerialPort and the FileStream have a Stream object I decided to use that to read and write to that port. The problem with that is if I send a RFID command to the MSR device the MSR device doesn't respond back with anything. So if I use this code int fromReader = ReaderStream.ReadByte(); my thread is blocked. It's a blocking call and requires a minimum of 1 byte to proceed. So I looked around and it appears the only solution is to use a separate thread and set a timeout. If the timeout happens then abort the thread.

        Thread t = new Thread(new ThreadStart(ReadFromStream));
        t.Start();
        if (!t.Join(timeout))
        {
            t.Abort();
        }

(t.Abort has been surrounded with a try/catch to no avail, since it didn't fix the problem I removed it)

ReadFromStream is Abstract method in RFID Device. Here is one of the implementations

    protected override void ReadFromStream()
    {
        var commandLength = USN3170Constants.MIN_RESPONSE_LENGTH;
        var response = new System.Collections.Generic.List<byte>(USN3170Constants.MIN_RESPONSE_LENGTH);
        for (int i = 0; i <= commandLength; i++)
        {
            int fromReader = ReaderStream.ReadByte();
            if (fromReader == -1) break; //at end of stream
            response.Add((byte)fromReader);

            if (response.Count > USN3170Constants.DATA_LENGTH_INDEX && response[USN3170Constants.DATA_LENGTH_INDEX] > 0)
            {
                commandLength = response[USN3170Constants.DATA_LENGTH_INDEX] + 3;
            }
        }

        streamBuffer = response.ToArray();
    }

(int fromReader = ReaderStream.ReadByte(); was surrounded with a try/catch. Only thing it caught was the aborted thread exception, so I took it out)

The above code is where I suspect the problem lies. The strange thing is, though, is that I have a unit test which I feel mimics rather well the Microsoft Test App.

(FYI QUADPORT is the FTDI chipset)

    PosExplorer posExplorer;
    DeviceCollection smartCardRWs;
    [Test]
    public void TestQuadPortOpen()
    {
        posExplorer = new PosExplorer();
        smartCardRWs = posExplorer.GetDevices(DeviceType.SmartCardRW, DeviceCompatibilities.CompatibilityLevel1);
        //if using quadport one item is the MSR and the other is the RFID
        //because of that one of them will fail. Currently the first Device in the collection is the the RFID, and the second is MSR
        Assert.GreaterOrEqual(smartCardRWs.Count, 2);
        //Hardware Id: QUADPORT\QUAD_SERIAL_INTERFACE
        foreach(DeviceInfo item in smartCardRWs)
        {
            Assert.AreEqual("QUADPORT\\QUAD_SERIAL_INTERFACE", item.HardwareId);
        }

        SmartCardRW rfidDevice = (SmartCardRW)posExplorer.CreateInstance(smartCardRWs[0]);
        SmartCardRW msrDevice = (SmartCardRW)posExplorer.CreateInstance(smartCardRWs[1]);

        rfidDevice.Open();
        Assert.AreNotEqual(ControlState.Closed, rfidDevice.State);
        rfidDevice.Close();

        try
        {
            msrDevice.Open();
            Assert.Fail("MSR Device is not a RFID Device");
        }
        catch
        {
            Assert.AreEqual(ControlState.Closed, msrDevice.State);
        }

        rfidDevice = null;
        msrDevice = null;
    }

When I run that test I do not get the SafeFileHandle exception. In fact the test passes.

So I am at a loss as to how to track down this bug. Since I'll be using this Service Object in a different program that I am also creating I'll probably end up using this code from this test in that program. However I feel that the Microsoft Test App is more or less the "Golden Standard". Is it really... probably not. But it does work good for my purposes, SO I feel it is a problem with my code and not theirs.

Any tricks on how I can narrow this down? FYI I've tried using the debugger but walking the Open Code the error does not occur. I also walked the Update Status Timer and it also does not throw the error. Once I hit continue then I'll get the exception. I turned of Just My Code and Loaded Symbols and it tells me "Source Information is missing from teh debug information for this module"

Sommers answered 20/12, 2013 at 17:11 Comment(4)
I'm not clear on the question (and I read the whole thing). Which line are you getting the error described in the subject? What is ReaderStream? I have a considerable amount of experience (and traumatizing headaches from) dealing with PInvoke and threading so I'd love to help but I'll need a bit of clarification. Also, I've been told numerous times (when I was asking similar questions) that Thread.Abort() is a bad idea. You'll probably want to try and avoid that one at all costs.Kc
@Kc ReaderStream is a Stream. In this context it is a SerialStream from a SerialPort. I'd love to talk to you about this in a chat if possibleSommers
I'm willing and I think SO has chat but I've never used it. Give me a link or an invite and I'll help where I canKc
@Kc not sure if this was the correct way of doing it, but here is the room chat.stackoverflow.com/rooms/43614/safefilehandle-troublesSommers
B
6

This problem (and in particular the reference to a SerialPort instance) sounds suspiciously like the problem documented at http://connect.microsoft.com/VisualStudio/feedback/details/140018/serialport-crashes-after-disconnect-of-usb-com-port.

As I understand it, in the case of a non-permanent SerialPort (like one associated with a USB device, for example) when the port "goes away" unexpectedly the underlying Stream associated with it gets disposed. If there is an active read or write operation on the port at the time a subsequent call to SerialPort.Close can lead to the exception you mention, however the exception is occurring in Microsoft's code running on a different thread and cannot be caught from within your code. (It will still be seen by any "last chance" exception handler you have bound to the UnhandledException event on the AppDomain.)

There seem to be two basic workaround styles in the linked document. In both instances, after opening the port you store a reference to the BaseStream instance for the open port. One workaround then suppresses garbage collection on that base stream. The other explicitly calls Close on the base stream, capturing any exceptions thrown during that operation, before calling Close on the SerialPort.

EDIT: For what it's worth, under the .NET framework V4.5, it appears that none of the documented workarounds on the Microsoft Connect site fully resolve the problem although they may be reducing the frequency with which it occurs. :-(

Burnight answered 3/6, 2014 at 20:39 Comment(1)
I am running into this problem now on .NET 4.5 and no workaround I've found on SO has changed the behaviour. If I remove the USB device soon after connecting and exchanging data, it always crashes with the SafeHandle exception. Unfortunately hotplugging is supposed to be a feature of this device...Keratosis
S
2

I had the same error when I used a thread to read from a SerialPort. Calling Interrupt on the thread occasionally caused the uncatchable ObjectDisposedException. After hours of debugging and carefully reading this:

https://blogs.msdn.microsoft.com/bclteam/2006/10/10/top-5-serialport-tips-kim-hamilton/

I realized that the problem is just this: NET 2.0 (and above) isn’t letting you get away with some things, such as attempting to cancel a SerialPort read by interrupting the thread accessing the SerialPort.

So before you call Thread.Interrupt() you have to close the COM... This will cause a catchable exception on the ReadByte operation.

Or you may use the ReadTimeout property on the SerialPort to avoid using a thread just to have a timeout.

Scheld answered 18/5, 2016 at 14:53 Comment(0)
M
2

I would like to post my case in which I had a similar issue trying to read from a serial port (virtual com driven by a Moxa RS232 to ethernet). Since I did have no chance to catch the ObjectDisposedException, the only solution was to increase the ReadTimeout property which was originally set to -1 (continuous reading). Setting the ReadTimeout to 100 millis solved this issue in my case.

EDIT
It is not the definitive solution: it can happen that if you close the application during a read attempt you can get the same uncatchable exception. My final solution is to kill the process of the application directly in the FormClosing event :

private void MyForm_FormClosing(object sender, FormClosingEventArgs e)
    {
        Process p = Process.GetCurrentProcess();
        p.Kill();
    }
Meprobamate answered 1/12, 2016 at 16:45 Comment(0)
E
1

Please take a look at this:

https://github.com/jcurl/SerialPortStream

I replaced System.IO.Ports with RJPC.IO.Ports, fixed up a couple parameter differences in the initialization, and all the problems went away with this issue.

Extortioner answered 28/10, 2016 at 12:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.