c# - ObjectDisposedException when closing SerialPort in .Net 2.0 -


i have c# windows forms application communicates usb dongle via com port. using serialport class in .net 2.0 communication, , serial port object open lifetime of application. application sends commands device , can receive unsolicited data device.

my problem occurs when form closed - (randomly, unfortunately) objectdisposedexception when attempting close com port. here windows stack trace:

system.objectdisposedexception unhandled   message=safe handle has been closed   source=system   objectname=""   stacktrace:        @ microsoft.win32.unsafenativemethods.setcommmask(safefilehandle hfile, int32 dwevtmask)        @ system.io.ports.serialstream.dispose(boolean disposing)        @ system.io.ports.serialstream.finalize()   innerexception:  

i have found posts people similar problems , have tried workaround [here][1]

[1]: http://zachsaw.blogspot.com/2010/07/net-serialport-woes.html although ioexception , did not stop problem.

my close() code follows:

        public void close()     {         try         {             console.writeline("******comport.close - basestream.close*******");             basestream.close();         }         catch (exception ex)         {             console.writeline("******comport.close basestream.close raised exception: " + ex + "*******");         }         try         {             _ondatareceived = null;             console.writeline("******comport.close - _serialport.close*******");             _serialport.close();         }         catch (exception ex)         {             console.writeline("******comport.close - _serialport.close raised exception: " + ex + "*******");         }                 } 

my logging showed execution never got beyond attempting close serialport's basestream (this in first try block), experimented removing line exception still thrown periodically - logging in second try block appeared exception happened. neither catch block catches exception.

any ideas?

update - adding full class:

    namespace my.utilities {     public interface iserialportobserver     {         void serialportwriteexception();     }      internal class comport : iserialport     {         private readonly iserialportobserver _observer;         readonly serialport _serialport;          private datareceiveddelegate _ondatareceived;         public event datareceiveddelegate ondatareceived         {             add { lock (_datareceivedlocker) { _ondatareceived += value; } }             remove { lock (_datareceivedlocker) { _ondatareceived -= value; } }                     }          private readonly object _datareceivedlocker = new object();         private readonly object _locker = new object();          internal comport()         {                      _serialport = new serialport { readtimeout = 10, writetimeout = 100, dtrenable = true };             _serialport.datareceived += datareceived;         }          internal comport(iserialportobserver observer) : this()         {             _observer = observer;                  }          private void datareceived(object sender, serialdatareceivedeventargs e)         {             datareceiveddelegate temp = null;              lock (_locker)             {                 lock (_datareceivedlocker)                 {                     temp = _ondatareceived;                 }                  string datareceived = string.empty;                 var sp = (serialport) sender;                  try                 {                     datareceived = sp.readexisting();                 }                 catch (exception ex)                 {                     logger.log(tracelevel.error, "comport.datareceived raised exception: " + ex);                 }                  if (null != temp && string.empty != datareceived)                 {                     try                     {                         temp(datareceived, tickprovider.gettickcount());                     }                     catch (exception ex)                     {                         logger.log(tracelevel.error, "comport.datareceived raised exception calling handler: " + ex);                     }                 }             }         }          public string port         {             set             {                 try                 {                     _serialport.portname = value;                 }                 catch (exception ex)                 {                     logger.log(tracelevel.error, "comport.port raised exception: " + ex);                 }             }         }          private system.io.stream comportstream = null;         public bool open()         {             setupserialportwithworkaround();             try             {                 _serialport.open();                 comportstream = _serialport.basestream;                 return true;             }             catch (exception ex)             {                 logger.log(tracelevel.warning, "comport.open raised exception: " + ex);                 return false;             }         }          public bool isopen         {                         {                 setupserialportwithworkaround();                 try                 {                     return _serialport.isopen;                 }                 catch(exception ex)                 {                     logger.log(tracelevel.error, "comport.isopen raised exception: " + ex);                 }                  return false;             }         }          internal virtual void setupserialportwithworkaround()         {             try             {                 //http://zachsaw.blogspot.com/2010/07/net-serialport-woes.html                 // class meant fix problem in .net causing objectdisposedexception.                 serialportfixer.execute(_serialport.portname);             }             catch (exception e)             {                 logger.log(tracelevel.info, "work around .net serialport object disposed exception failed : " + e + " still attempt open port normal");             }         }          public void close()         {             try             {                 comportstream.close();             }             catch (exception ex)             {                 logger.log(tracelevel.error, "comportstream.close raised exception: " + ex);             }             try             {                 _ondatareceived = null;                 _serialport.close();             }             catch (exception ex)             {                 logger.log(tracelevel.error, "comport.close raised exception: " + ex);             }                     }          public void writedata(string adata, datareceiveddelegate handler)         {             try             {                 ondatareceived += handler;                 _serialport.write(adata + "\r\n");             }             catch (exception ex)             {                 logger.log(tracelevel.error, "comport.writedata raised exception: " + ex);                                  if (null != _observer)                 {                     _observer.serialportwriteexception();                 }             }         }     }     } 

note: current findings have been tested on .net framework 4.0 32-bit on windows 7, please feel free comment if works on other versions.

edit: tl;dr: here's crux of workaround. see below explanation. don't forget use serialportfixer when opening serialport well. ilog log4net.

static readonly ilog s_log = logmanager.gettype("serialworkaroundlogger");  static void safedisconnect(serialport port, stream internalserialstream) {     gc.suppressfinalize(port);     gc.suppressfinalize(internalserialstream);      shutdowneventloophandler(internalserialstream);      try     {         s_log.debugformat("disposing internal serial stream");         internalserialstream.close();     }     catch (exception ex)     {         s_log.debugformat(             "exception in serial stream shutdown of port {0}: {1}", port.portname, ex);     }      try     {         s_log.debugformat("disposing serial port");         port.close();     }     catch (exception ex)     {         s_log.debugformat("exception in port {0} shutdown: {1}", port.portname, ex);     } }  static void shutdowneventloophandler(stream internalserialstream) {     try     {         s_log.debugformat("working around .net serialport class dispose bug");          fieldinfo eventrunnerfield = internalserialstream.gettype()             .getfield("eventrunner", bindingflags.nonpublic | bindingflags.instance);          if (eventrunnerfield == null)         {             s_log.warnformat(                 "unable find eventlooprunner field. "                 + "serialport workaround failure. application may crash after "                 + "disposing serialport unless .net 1.1 unhandled exception "                 + "policy enabled application's config file.");         }         else         {             object eventrunner = eventrunnerfield.getvalue(internalserialstream);             type eventrunnertype = eventrunner.gettype();              fieldinfo endeventloopfieldinfo = eventrunnertype.getfield(                 "endeventloop", bindingflags.instance | bindingflags.nonpublic);              fieldinfo eventloopendedsignalfieldinfo = eventrunnertype.getfield(                 "eventloopendedsignal", bindingflags.instance | bindingflags.nonpublic);              fieldinfo waitcommeventwaithandlefieldinfo = eventrunnertype.getfield(                 "waitcommeventwaithandle", bindingflags.instance | bindingflags.nonpublic);              if (endeventloopfieldinfo == null                 || eventloopendedsignalfieldinfo == null                 || waitcommeventwaithandlefieldinfo == null)             {                 s_log.warnformat(                     "unable find eventlooprunner internal wait handle or loop signal fields. "                     + "serialport workaround failure. application may crash after "                     + "disposing serialport unless .net 1.1 unhandled exception "                     + "policy enabled application's config file.");             }             else             {                 s_log.debugformat(                     "waiting serialport internal eventlooprunner thread finish...");                  var eventloopendedwaithandle =                     (waithandle)eventloopendedsignalfieldinfo.getvalue(eventrunner);                 var waitcommeventwaithandle =                     (manualresetevent)waitcommeventwaithandlefieldinfo.getvalue(eventrunner);                  endeventloopfieldinfo.setvalue(eventrunner, true);                  // event loop handler resets wait handle                 // before exiting loop , hangs (in case of usb disconnect)                 // in case takes long, brute-force out of wait                 // setting handle again.                                 {                     waitcommeventwaithandle.set();                 } while (!eventloopendedwaithandle.waitone(2000));                  s_log.debugformat("wait completed. safe continue disposal.");             }         }     }     catch (exception ex)     {         s_log.errorformat(             "serialport workaround failure. application may crash after "             + "disposing serialport unless .net 1.1 unhandled exception "             + "policy enabled application's config file: {0}",             ex);     } } 

i've wrestled couple of days in recent project.

there many different bugs (i've seen far) .net serialport class lead of headaches on web.

  1. the missing dcb struct flag here: http://zachsaw.blogspot.com/2010/07/net-serialport-woes.html 1 fixed serialportfixer class, credits go author one.

  2. when usb serial device removed, when closing serialportstream, eventlooprunner asked stop , serialport.isopen returns false. upon disposal property checked , closing internal serial stream skipped, keeping original handle open indefinitely (until finalizer runs leads next problem).

    the solution 1 manually close internal serial stream. can reference serialport.basestream before exception has happened or reflection , getting "internalserialstream" field.

  3. when usb serial device removed, closing internal serial stream throws exception , closes internal handle without waiting eventlooprunner thread finish, causing uncatchable objectdisposedexception background event loop runner thread later on when stream's finalizer runs (which oddly avoids throwing exception still fails wait eventlooprunner).

    symptom of here: https://connect.microsoft.com/visualstudio/feedback/details/140018/serialport-crashes-after-disconnect-of-usb-com-port

    the solution manually ask event loop runner stop (via reflection) , waiting finish before closing internal serial stream.

  4. since dispose throws exceptions, finalizer not suppressed. solvable:

    gc.suppressfinalize(port); gc.suppressfinalize(port.basestream);

here's class wraps serial port , fixes these issues: http://pastebin.com/kmkevzr8

with workaround class, reverting .net 1.1 unhandled exception behavior unnecessary , works excellent stability.

this first contribution, please excuse me if i'm not doing right. hope helps someone.


Comments