我有一个 C# windows 窗体应用程序,它通过 COM 端口与 USB dongle 通信。我使用 .Net 2.0 中的 SerialPort 类进行通信,并且串行端口对象在应用程序的生命周期内处于打开状态。应用程序向设备发送命令,也可以从设备接收未经请求的数据。
我的问题发生在窗体关闭时 - 我在尝试关闭 COM 端口时(不幸的是随机地)得到一个 ObjectDisposedException。这是 Windows 堆栈跟踪:
System.ObjectDisposedException was unhandled
Message=Safe handle has been closed
Source=System
ObjectName=""
StackTrace:
at Microsoft.Win32.UnsafeNativeMethods.SetCommMask(SafeFileHandle hFile, Int32 dwEvtMask)
at System.IO.Ports.SerialStream.Dispose(Boolean disposing)
at System.IO.Ports.SerialStream.Finalize()
InnerException:
我找到了有类似问题的人的帖子,并尝试了[此处][1] 的解决方法
[1]: http://zachsaw.blogspot.com/2010/07/net-serialport-woes.html尽管这是针对 IOException 的,但并没有解决问题。
我的Close()代码如下:
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 + "*******");
}
}
我的日志记录表明,执行从来没有超越尝试关闭 SerialPort 的 BaseStream(这是在第一个 try block 中),所以我尝试删除这一行,但仍然定期抛出异常 -登录第二个 try block 出现然后异常发生。两个 catch block 都没有捕获异常。
有什么想法吗?
更新 - 添加全类:
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
{
get
{
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
// This class is meant to fix the problem in .Net that is causing the ObjectDisposedException.
SerialPortFixer.Execute(_serialPort.PortName);
}
catch (Exception e)
{
Logger.Log(TraceLevel.Info, "Work around for .Net SerialPort object disposed exception failed with : " + e + " Will still attempt open port as 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();
}
}
}
}
}
最佳答案
注意:目前的发现仅在 Windows 7 上的 .NET Framework 4.0 32 位上进行了测试,如果它在其他版本上有效,请随时发表评论。
编辑: 长话短说: 这是解决方法的关键。请参阅下面的解释。 不要忘记使用 SerialPortFixer打开 SerialPort 时也是如此。 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 to find EventLoopRunner field. "
+ "SerialPort workaround failure. Application may crash after "
+ "disposing SerialPort unless .NET 1.1 unhandled exception "
+ "policy is enabled from the 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 to find the EventLoopRunner internal wait handle or loop signal fields. "
+ "SerialPort workaround failure. Application may crash after "
+ "disposing SerialPort unless .NET 1.1 unhandled exception "
+ "policy is enabled from the application's config file.");
}
else
{
s_Log.DebugFormat(
"Waiting for the SerialPort internal EventLoopRunner thread to finish...");
var eventLoopEndedWaitHandle =
(WaitHandle)eventLoopEndedSignalFieldInfo.GetValue(eventRunner);
var waitCommEventWaitHandle =
(ManualResetEvent)waitCommEventWaitHandleFieldInfo.GetValue(eventRunner);
endEventLoopFieldInfo.SetValue(eventRunner, true);
// Sometimes the event loop handler resets the wait handle
// before exiting the loop and hangs (in case of USB disconnect)
// In case it takes too long, brute-force it out of its wait by
// setting the handle again.
do
{
waitCommEventWaitHandle.Set();
} while (!eventLoopEndedWaitHandle.WaitOne(2000));
s_Log.DebugFormat("Wait completed. Now it is safe to continue disposal.");
}
}
}
catch (Exception ex)
{
s_Log.ErrorFormat(
"SerialPort workaround failure. Application may crash after "
+ "disposing SerialPort unless .NET 1.1 unhandled exception "
+ "policy is enabled from the application's config file: {0}",
ex);
}
}
我在最近的一个项目中为此苦苦挣扎了几天。
.NET SerialPort 类有许多不同的错误(到目前为止我已经看到)导致网络上所有令人头疼的问题。
此处缺少 DCB 结构标志:http://zachsaw.blogspot.com/2010/07/net-serialport-woes.html 这一个由 SerialPortFixer 类修复,该功劳归作者所有。
当移除 USB 串行设备时,关闭 SerialPortStream 时,会要求 eventLoopRunner 停止并且 SerialPort.IsOpen 返回 false。在处置时检查此属性并跳过关闭内部串行流,从而使原始句柄无限期地保持打开状态(直到终结器运行导致下一个问题)。
这个解决方案是手动关闭内部串行流。我们可以在异常发生前通过 SerialPort.BaseStream 或者通过反射获取它的引用 并获取“internalSerialStream”字段。
当 USB 串行设备被移除时,关闭内部串行流会引发异常并关闭内部句柄而不等待其 eventLoopRunner 线程完成,从而导致稍后在后台事件循环运行器线程中出现无法捕获的 ObjectDisposedException流的终结器运行(这奇怪地避免了抛出异常但仍然无法等待 eventLoopRunner)。
解决方案是手动要求事件循环运行器停止(通过反射)并在关闭内部串行流之前等待它完成。
由于 Dispose 会抛出异常,因此不会抑制终结器。这很容易解决:
GC.SuppressFinalize(端口); GC.SuppressFinalize(port.BaseStream);
这是一个包装串行端口并修复所有这些问题的类: http://pastebin.com/KmKEVzR8
有了这个变通类,就不需要恢复到 .NET 1.1 未处理的异常行为,而且它具有出色的稳定性。
这是我的第一个贡献,如果我做的不对,请原谅。我希望它对某人有所帮助。
关于c# - 在 .Net 2.0 中关闭 SerialPort 时出现 ObjectDisposedException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8927410/
作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代
我正在用Ruby编写一个简单的程序来检查域列表是否被占用。基本上它循环遍历列表,并使用以下函数进行检查。require'rubygems'require'whois'defcheck_domain(domain)c=Whois::Client.newc.query("google.com").available?end程序不断出错(即使我在google.com中进行硬编码),并打印以下消息。鉴于该程序非常简单,我已经没有什么想法了-有什么建议吗?/Library/Ruby/Gems/1.8/gems/whois-2.0.2/lib/whois/server/adapters/base.
我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po
是的,我知道最好使用webmock,但我想知道如何在RSpec中模拟此方法:defmethod_to_testurl=URI.parseurireq=Net::HTTP::Post.newurl.pathres=Net::HTTP.start(url.host,url.port)do|http|http.requestreq,foo:1endresend这是RSpec:let(:uri){'http://example.com'}specify'HTTPcall'dohttp=mock:httpNet::HTTP.stub!(:start).and_yieldhttphttp.shou
我正在尝试编写一个将文件上传到AWS并公开该文件的Ruby脚本。我做了以下事情:s3=Aws::S3::Resource.new(credentials:Aws::Credentials.new(KEY,SECRET),region:'us-west-2')obj=s3.bucket('stg-db').object('key')obj.upload_file(filename)这似乎工作正常,除了该文件不是公开可用的,而且我无法获得它的公共(public)URL。但是当我登录到S3时,我可以正常查看我的文件。为了使其公开可用,我将最后一行更改为obj.upload_file(file
如何在ruby中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL
我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha
我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里