我有2个表单,一个是MainForm
,第二个是DebugForm
。 MainForm有一个按钮,用于设置和显示DebugForm,并将引用传递给已打开的SerialPort:
private DebugForm DebugForm; //Field
private void menuToolsDebugger_Click(object sender, EventArgs e)
{
if (DebugForm != null)
{
DebugForm.BringToFront();
return;
}
DebugForm = new DebugForm(Connection);
DebugForm.Closed += delegate
{
WindowState = FormWindowState.Normal;
DebugForm = null;
};
DebugForm.Show();
}
在DebugForm中,我附加了一个方法来处理serialport连接的DataReceived
事件(在DebugForm的构造函数中):
public DebugForm(SerialPort connection)
{
InitializeComponent();
Connection = connection;
Connection.DataReceived += Connection_DataReceived;
}
然后在Connection_DataReceived
方法中,我更新DebugForm中的TextBox,即使用Invoke进行更新:
private void Connection_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
_buffer = Connection.ReadExisting();
Invoke(new EventHandler(AddReceivedPacketToTextBox));
}
但我有一个问题。一旦我关闭DebugForm,它就会在ObjectDisposedException
行上抛出Invoke(new EventHandler(AddReceivedPacketToTextBox));
。
我该如何解决这个问题?欢迎任何提示/帮助!
更新
我发现如果在按钮事件中单击删除事件,并在该按钮单击中关闭表单,一切正常,我的调试表关闭,没有任何异常......多么奇怪!
private void button1_Click(object sender, EventArgs e)
{
Connection.DataReceived -= Connection_DebugDataReceived;
this.Close();
}
答案 0 :(得分:6)
关闭表单会释放Form对象,但不能强制删除其他类对其进行的引用。当您为事件注册表单时,您基本上是将表单对象引用到事件源(在这种情况下为SerialPort
实例)。
这意味着,即使您的表单已关闭,事件源(您的SerialPort
对象)仍在向表单实例发送事件,并且仍在运行处理这些事件的代码。问题是,当此代码尝试更新已处置的表单(设置其标题,更新其控件,调用Invoke
,& c。)时,您将收到此异常。
因此,您需要做的是确保在表单关闭时取消注册事件。这就像检测表单正在关闭并取消注册Connection_DataReceived
事件处理程序一样简单。您可以通过覆盖OnFormClosing
方法并在那里取消注册事件来检测表单是否正在关闭:
protected override OnFormClosing(FormClosingEventArgs args)
{
Connection.DataReceived -= Connection_DataReceived;
}
我还建议将事件 registration 移动到覆盖OnLoad
方法,否则它可能会在表单完全构建之前收到事件,这可能会导致混乱的异常。
答案 1 :(得分:2)
您尚未显示AddReceivedPacketToTextBox
方法的代码。
您可以尝试使用该方法检查已处理的表单:
private void AddReceivedPacketToTextBox(object sender, EventArgs e)
{
if (this.IsDisposed) return;
...
}
关闭表单时分离DataReceived
事件处理程序可能是一个好主意,但还不够:还有一个竞争条件,这意味着在表单关闭后可以调用AddReceivedPacketToTextBox
/设置。序列如下:
我发现如果在按钮事件中单击删除事件,并在该按钮单击中关闭表单,一切正常,我的调试表关闭,没有任何异常......多么奇怪!
这并不奇怪。多线程错误(“Heisenbugs”)与时序相关,这种小的变化会影响时序。但它不是一个强大的解决方案。
答案 2 :(得分:0)
可以通过添加计时器来解决问题:
bool formClosing = false;
private void Connection_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (formClosing) return;
_buffer = Connection.ReadExisting();
Invoke(new EventHandler(AddReceivedPacketToTextBox));
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
base.OnFormClosing(e);
if (formClosing) return;
e.Cancel = true;
Timer tmr = new Timer();
tmr.Tick += Tmr_Tick;
tmr.Start();
formClosing = true;
}
void Tmr_Tick(object sender, EventArgs e)
{
((Timer)sender).Stop();
this.Close();
}
感谢来自MSDN
的JohnWein