12

I am using a .NET 4 SerialPort object to talk to a device attached to COM1.

When I am done with the device, I call Close on the SerialPort. I do not call Dispose, but I believe that Close and Dispose are synonymous here.

Usually this works just fine.

Sometimes, however, I get the following exception some time later (The times I've seen range from 5 ms to 175 ms):

System.ObjectDisposedException: Safe handle has been closed
     at System.Runtime.InteropServices.SafeHandle.DangerousAddRef(Boolean& success)
     at System.StubHelpers.StubHelpers.SafeHandleAddRef(SafeHandle pHandle, Boolean& success)
     at Microsoft.Win32.UnsafeNativeMethods.GetOverlappedResult(SafeFileHandle hFile, NativeOverlapped* lpOverlapped, Int32& lpNumberOfBytesTransferred, Boolean bWait)
     at System.IO.Ports.SerialStream.EventLoopRunner.WaitForCommEvent()
     at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
     at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
     at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
     at System.Threading.ThreadHelper.ThreadStart()

None of my code is on this stack.

I found http://blog.zachsaw.com/2010/07/serialport-ioexception-workaround-in-c.html, but the solution there did not work. On further inspection, the issue there is an IOException, not an ObjectDisposedException.

There are a plethora of posts concerning issues observed when a USB-to-serial device is unplugged, but COM1 is onboard, so it isn't vanishing unexpectedly.

The problem here is also not my issue; the SerialPort is kept alive for the duration of its use, and is closed only when I am done talking to the device. (Once I am done, the device is in a state where it will not transmit any further data.)

SLaks suggests setting a breakpoint on the entrance to SafeHandle.Dispose, to determine when I'm disposing something I shouldn't be, but I strike that breakpoint dozens of times. Three times are called by my single call to SerialPort.Close, when I am done using the serial device, and about half the rest are in the GC thread. The remainder seem to be related to WPF UI elements.

I am now at a loss. Where do I go from here?

Is there a way to determine which SafeHandle belongs to which object, so I can be certain I'm not disposing it unexpectedly?
Is there some incantation other than Close I need to properly shut down a SerialPort?

Community
  • 1
  • 1
DaleStan
  • 595
  • 2
  • 6
  • 19
  • I've had this issue before, but never did successfully track it down. In any case, read my answer here: http://stackoverflow.com/questions/6348486/net-serial-port-woes-converting-c-code-to-vb/6350319#6350319 Save yourself a lot of time and hassle. Even in .NET 4.0, the built-in serial class simply isn't reliable enough for using. I do hope you find a direct solution to your question however. – Brad Aug 18 '11 at 20:47
  • Are you 110% sure that this runs on .NET 4.0? This should not happen. – Hans Passant Aug 18 '11 at 20:57
  • There is another one on .NET 4.0 at: http://stackoverflow.com/questions/3808885/net-4-serial-port-objectdisposedexception-on-windows-7-only – Eddy Aug 18 '11 at 21:10
  • Could you show us (preferably short) sample code where this issue occurs? – svick Aug 18 '11 at 21:25
  • @hanspassant: I'm pretty sure. The target framework for the binary and all dlls I control is set to ".NET Framework 4". I know there are third-party DLLs compiled for .NET 2.0, though. Is that sufficient proof, or is there somewhere else I need to look next time I see this exception? – DaleStan Aug 19 '11 at 13:05
  • One thing I recently noticed is that the SerialPort has a .Encoding. I fought for a while with the default (UTF-8, I assume) encoding, as the device transmits 0xAA and 0xBB bytes as delimiters. Could a erroring decoder cause this? If so, what decoder should I be using? Encoding.ASCII calls itself 7-bit ASCII. If the 7-bit is true, this will break just as badly as UTF-8, and I can't find any Encoding that is obviously an 8-bit don't-change-the-bytes encoding. – DaleStan Aug 19 '11 at 13:13
  • 1
    Oh dear, I see it now. There is a race condition in the code that waits for something to happen on the serial port. You increase the odds of triggering it considerably by closing the port in response to an event. The workaround is to not close the port until you exit the program. Which is already automatic btw. – Hans Passant Aug 19 '11 at 13:58

2 Answers2

4

I've had this issue too, and since I started using the following two rules I've never seen it again.

  1. Always call Close() followed by Dispose().
  2. Never reuse a SerialPort object, always create a new one when a port needs to be reopened.

I know, they aren't much news, but its been working for me.

Per
  • 1,074
  • 6
  • 19
  • I guess I'll mark this is the answer. It turns out I lied in my question, which is also why I couldn't produce a test case. I was talking up to ten different external devices, and in the process of disconnecting a different one, I set my serial port reference to null, instead of the reference to the device I'd actually disconnected. I have not observed this none-of-my-code `ObjectDisposedException` since I fixed this premature nulling. – DaleStan Oct 11 '11 at 14:13
  • 4
    @Per `Close()` method calls `Dispose()`; therefore, seems unnecessary – Jake Berger Jun 14 '12 at 20:31
  • Never reusing and still encounters this issue. – Winter Jul 20 '17 at 17:40
0

The call to disposed is documented behavior (see here) - I guess you try to read/write after Close (maybe in another thread). I would suggest wrapping the calls in a seperate class and setting a "closed"-flag. Then you should be able to find the problem rather fast.

Random Dev
  • 51,810
  • 9
  • 92
  • 119
  • If I were trying to read/write from another thread, shouldn't there be some of my code on the stack? – DaleStan Aug 19 '11 at 13:12
  • maybe - just guessing - what more can I do without seeing all of it and without the ability to debug the code myself? Hafe you tried the "wrapper"-approach? – Random Dev Aug 19 '11 at 13:34