C#学习教程:在.Net2.0中关闭SerialPort时出现ObjectDisposedException我使用.Net2.0中的SerialPort类进行通信,并且串行端口对象在应用程序的生命周期内处于打开状态。应用程序向设备发送命令,也可以从设备接收未经请求的数据。关闭表单时出现问题-我(不幸的是)在尝试关闭COM端口时收到ObjectDisposedException。这是Windows堆栈跟踪:System.ObjectDisposedException未处理Message=SafehandlehasbeenclosedSource=SystemObjectName=""StackTrace:atMicrosoft.Win32.UnsafeNativeMethods.SetCommMask(SafeFileHandlehFile,Int32dwEvtMask)atSystem.IO.Ports。SerialStream.Dispose(Booleandisposing)atSystem.IO.Ports.SerialStream.Finalize()InnerException:我发现了一个有类似问题的人的帖子并尝试了一种解决方法[这里][1][1]:http://zachsaw.blogspot.com/2010/07/net-serialport-woes.html虽然这是针对IOException的,但不会阻止该问题。我的Close()代码如下:publicvoidClose(){try{Console.WriteLine("******ComPort.Close-baseStream.Close******");baseStream.Close();}catch(Exceptionex){Console.WriteLine("******ComPort.ClosebaseStream.Closeraisedexception:"+ex+"********");}尝试{_onDataReceived=null;控制台.WriteLine("******ComPort.Close-_serialPort.Close********");_serialPort.Close();}catch(Exceptionex){Console.WriteLine("******ComPort.Close-_serialPort.Close引发异常:"+ex+"********");我的日志记录显示执行永远不会超出尝试关闭SerialPort的BaseStream(在第一个try块中),所以我尝试删除这一行但异常仍然会定期抛出-在第二个try块中登录然后异常发生了。没有一个catch块捕获异常。有什么想法吗?更新–添加完整课程:}内部类ComPort:ISerialPort{privatereadonlyISerialPortObserver_observer;只读串行端口_serialPort;私人数据接收委托_onDataReceived;公共事件DataReceivedDelegateOnDataReceived{添加{锁(_dataReceivedLocker){_onDataReceived+=值;}}删除{锁(_dataReceivedLocker){_onDataReceived-=值;}}}私有只读对象_dataReceivedLocker=newobject();私有只读对象_locker=newobject();内部ComPort(){_serialPort=newSerialPort{ReadTimeout=10,WriteTimeout=100,DtrEnable=true};_serialPort.DataReceived+=数据接收;}内部ComPort(ISerialPortObserver观察者):this(){_observer=observer;}privatevoidDataReceived(objectsender,SerialDataReceivedEventArgse){DataReceivedDelegatetemp=null;锁(_locker){锁(_dataReceivedLocker){temp=_onDataReceived;}stringdataReceived=string.Empty;varsp=(SerialPort)发件人;尝试{dataReceived=sp.ReadExisting();}catch(Exceptionex){Logger.Log(TraceLevel.Error,"ComPort.DataReceivedraisedexception:"+ex);}if(null!=temp&&string.Empty!=dataReceived){try{temp(dataReceived,TickProvider.GetTickCount());}catch(Exceptionex){Logger.Log(TraceLevel.Error,"ComPort.DataReceived引发异常调用处理程序:"+ex);}}}}publicstringPort{set{try{_serialPort.PortName=value;}catch(Exceptionex){Logger.Log(TraceLevel.Error,"ComPort.Port引发异常:"+ex);}}}privateSystem.IO.StreamcomPortStream=null;publicboolOpen(){SetupSerialPortWithWorkaround();尝试{_serialPort.Open();comPortStream=_serialPort.BaseStream;返回真;}catch(Exceptionex){Logger.Log(TraceLevel.Warning,"ComPort.Openraisedexception:"+ex);返回假;}}publicboolIsOpen{get{SetupSerialPortWithWorkaround();尝试{返回_serialPort.IsOpen;}catch(Exceptionex){Logger.Log(TraceLevel.Error,"ComPort.IsOpen引发异常:"+ex);}返回假;}}internalvirtualvoidSetupSerialPortWithWorkaround(){try{//http://zachsaw.blogspot.com/2010/07/net-serialport-woes.html//此类旨在修复.Net中导致的问题ObjectDisposedException。SerialPortFixer.Execute(_serialPort.PortName);}catch(Exceptione){Logger.Log(TraceLevel.Info,"Workaroundfor.NetSerialPortobjectdisposedexceptionfailedwith:"+e+"Willstilltr??yopenportasnormal");}}publicvoidClose(){try{comPortStream.Close();}catch(Exceptionex){Logger.Log(TraceLevel.Error,"ComPortStream.Closeraisedexception:"+ex);}尝试{_onDataReceived=null;_serialPort.Close();}catch(Exceptionex){Logger.Log(TraceLevel.Error,"ComPort.Closeraisedexception:"+ex);}}publicvoidWriteData(stringaData,DataReceivedDelegatehandler){try{OnDataReceived+=handler;_serialPort.Write(aData+"rn");}catch(Exceptionex){Logger.Log(TraceLevel.Error,"ComPort.WriteDataraisedexception:"+ex);如果(空!=_observer){_observer.SerialPortWriteException();}}}}}注意:目前的发现仅在Windows7上的32位.NETFramework4.0上进行了测试,如果适用于其他版本,请随时评论编辑:TL;DR:这是解决方法的关键。请参阅下面的说明。打开SerialPort时不要忘记使用SerialPortFixer。ILog来自log4net。静态只读ILogs_Log=LogManager.GetType("SerialWorkaroundLogger");静态无效SafeDisconnect(SerialPort端口,StreaminternalSerialStream){GC.SuppressFinalize(端口);GC.SuppressFinalize(internalSerialStream);ShutdownEventLoopHandler(内部串行流);try{s_Log.DebugFormat("处理内部串行流");internalSerialStream.Close();}catch(Exceptionex){s_Log.DebugFormat("端口{0}的串行流关闭异常:{1}",port.PortName,ex);}try{s_Log.DebugFormat("配置串口");端口.关闭();}catch(Exceptionex){s_Log.DebugFormat("端口{0}关闭异常:{1}",port.PortName,ex);}}staticvoidShutdownEventLoopHandler(StreaminternalSerialStream){try{s_Log.DebugFormat("Workingaround.NETSerialPortclassDisposebug");FieldInfoeventRunnerField=internalSerialStream.GetType().GetField("eventRunner",BindingFlags.NonPublic|BindingFlags.Instance);如果(eventRunnerField==null){s_Log.WarnFormat("无法找到EventLoopRunner字段。"+"SerialPort解决方法失败。应用程序可能会在处理SerialPort后崩溃,除非.NET1.1未处理的异常"+"从应用程序的配置文件中启用策略。");}else{objecteventRunner=eventRunnerField.GetValue(internalSerialStream);输入eventRunnerType=eventRunner.GetType();FieldInfoendEventLoopF??ieldInfo=eventRunnerType.GetField("endEventLoop",BindingFlags.Instance|BindingFlags.NonPublic);FieldInfoeventLoopEndedSignalFieldInfo=eventRunnerType.GetField("eventLoopEndedSignal",BindingFlags.Instance|BindingFlags.NonPublic);FieldInfowaitCommEventWaitHandleFieldInfo=eventRunnerType.GetField("waitCommEventWaitHandle",BindingFlags.Instance|BindingFlags.NonPublic);if(endEventLoopF??ieldInfo==null||eventLoopEndedSignalFieldInfo==null||waitCommEventWaitHandleFieldInfo==null){s_Log.WarnFormat("无法找到EventLoopRunn呃内部等待句柄或循环信号字段。"+"SerialPort解决方法失败。除非.NET1.1未处理异常,否则应用程序可能会在"+"处理SerialPort后崩溃"+"从应用程序的配置文件启用策略。");}else{s_Log.DebugFormat("等待SerialPort内部EventLoopRunner线程完成...");vareventLoopEndedWaitHandle=(WaitHandle)eventLoopEndedSignalFieldInfo.GetValue(eventRunner);varwaitCommEventWaitHandle=(ManualResetEvent)waitCommEventWaitHandleFieldInfo.GetValue(eventRunner);endEventLoopF??ieldInfo.SetValue(eventRunner,true);//有时会重置等待句柄/处理程序/在退出循环并挂起之前(在USB断开连接的情况下)//如果它花费的时间太长,通过//再次设置句柄来强行使其退出等待。do{waitCommEventWaitHandle.Set();}while(!eventLoopEndedWaitHandle.WaitOne(2000));s_Log.DebugFormat("等待完成。现在可以安全地继续显示了osal.");}}}catch(Exceptionex){s_Log.ErrorFormat("SerialPort解决方法失败。应用程序可能会在"+"处理SerialPort后崩溃,除非.NET1.1未处理的异常"+"从应用程序的配置文件中启用策略:{0}",ex);}}在最近的一个项目中,我已经在这里工作了几天,现在使用.NETSerialPort类,有许多不同的错误(到目前为止我已经看到)导致所有问题网络。缺少的DCB结构标志位于:http://zachsaw.blogspot.com/2010/07/net-serialport-woes.html这是由SerialPortFixer类修复的,归功于作者。删除时关闭SerialPortStreamUSB串行设备,要求eventLoopRunner停止,SerialPort.IsOpen返回false。处理此属性后,将跳过关闭内部串行流,使原始句柄无限期打开(直到终结器运行导致下一个问题)。这个resolves的解决方法是手动关闭内部串行流,我们可以在异常发生前通过SerialPort.BaseStream获取到它的引用,或者通过反射获取到“internalSerialStream”字段。当USB串行设备被移除时,关闭内部串行流将抛出异常并关闭内部句柄而不等待其eventLoopRunner线程完成,因此在流的终结器运行时从后台事件循环运行程序线程会导致无法捕获的ObjectDisposedException(奇怪地避免抛出异常,但仍然无法等待eventLoopRunner)。症状在这里:https://connect.microsoft.com/VisualStudio/feedback/details/140018/serialport-crashes-after-disconnect-of-usb-com-port解决方案是手动要求事件循环运行器停止(通过反射)并在关闭内部串行流之前等待它完成。由于Dispose抛出异常,因此不会抑制终结器。这是一个简单的修复:GC.SuppressFinalize(port);GC.SuppressFinalize(port.BaseStream);这里有一个包装串行端口并修复所有这些问题的类:http://pastebin.com/KmKEVzR8使用这个解决方法类,不需要恢复到.NET1.1未处理的异常行为,并且它具有出色的稳定性。这是我的第一次贡献,所以如果我做错了什么,请原谅。我希望它能帮助别人。是的,SerialPort类中存在一个缺陷,可能会导致这种崩溃。SerialPort在调用Open()时启动一个线程。该线程监视事件的端口,例如,您获取DataReceived事件的方式。当您调用BaseStream.Close()或Close()或Dispose()方法(它们都做同样的事情)时,SerialPort只是要求线程退出但不等待它退出。这可能会导致各种问题。记录在案,您永远不应该在关闭端口后立即打开()端口。但是这里发生的是您的程序在Close()调用后立即退出或垃圾收集。它运行终结器,它也尝试关闭句柄。它仍然打开,因为工作线程仍在使用它。现在可以穿线了,这不是正确的互锁。当worker在终结器线程尝试执行相同操作之前设法关闭句柄并退出时,就会发生kaboom。异常是不可捕获的,因为它发生在终结器线程中,CLR会在该线程中止程序。自2.0以来的每个.NET版本都对类进行了小的更改以修复SerialPort问题。到目前为止,如果您仍在使用.NET2.0,最好的办法就是不要实际调用Close()。无论如何它都会自动发生,终结器会处理它。即使由于某种原因(硬崩溃或程序中止)没有发生这种情况,Windows也会确保端口已关闭。我知道这是一个很老的问题。我最近遇到了这个问题,在搜索解决方案之后,根据发行说明,看起来这个问题最终在.NETFramework4.7中得到了解决。https://github.com/Microsoft/dotnet/blob/master/releases/net47/dotnet47-changes.md修复了SerialPort中的错误,在该错误中,在执行期间拔掉设备可能会导致SerialStream类中的内存泄漏。[288363]以上是C#学习教程:.Net2.0中关闭SerialPort时发生ObjectDisposedException。如果对大家有用,需要进一步了解C#学习教程,希望大家多加关注——本文来自网络收集,不代表立场,如涉及侵权,请点击有权联系管理员删除。如需转载请注明出处:
