I have 2 forms, one is MainForm
and second is DebugForm
. The MainForm has a button that sets up and shows the DebugForm like this, And passes a reference to an already opened 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();
}
In the DebugForm, I append a method to handle the DataReceived
event of the serialport connection (in DebugForm's constructor):
public DebugForm(SerialPort connection)
{
InitializeComponent();
Connection = connection;
Connection.DataReceived += Connection_DataReceived;
}
Then in the Connection_DataReceived
method, I update a TextBox in the DebugForm, that is using Invoke to do the update:
private void Connection_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
_buffer = Connection.ReadExisting();
Invoke(new EventHandler(AddReceivedPacketToTextBox));
}
But I have a problem. As soon as I close the DebugForm, it throws an ObjectDisposedException
on the Invoke(new EventHandler(AddReceivedPacketToTextBox));
Line.
How can I fix this? Any tips/helps are welcome!
UPDATE
I found out if I remove the event in a button event click , and close the form in that button click, everything is fine and my debugform gets closed without any exception...how odd!
private void button1_Click(object sender, EventArgs e)
{
Connection.DataReceived -= Connection_DebugDataReceived;
this.Close();
}
Closing a form disposes of the Form object but cannot forcibly remove references that other classes have to it. When you register your form for events, you are basically giving a reference to your form object to the source of the events (the SerialPort
instance in this case).
This means that, even though your form is closed, the event source (your SerialPort
object) is still sending events to the form instance and the code to handle these events is still being run. The problem then is that when this code tries to update the disposed form (set its title, update its controls, call Invoke
, &c.) you will get this exception.
So what you need to do is ensure that the event gets deregistered when your form closes. This is as simple as detecting that the form is closing and unregister the Connection_DataReceived
event handler. Handily you can detect the form is closing by overriding the OnFormClosing
method and unregistering the event in there:
protected override OnFormClosing(FormClosingEventArgs args)
{
Connection.DataReceived -= Connection_DataReceived;
}
I would also recommend moving the event registration to an override of the OnLoad
method as otherwise it may receive events before the form has been fully constructed which could cause confusing exceptions.
You haven't shown the code for the AddReceivedPacketToTextBox
method.
You could try checking for a disposed form in that method:
private void AddReceivedPacketToTextBox(object sender, EventArgs e)
{
if (this.IsDisposed) return;
...
}
Detaching the DataReceived
event handler when closing the form is probably a good idea, but isn't sufficient: there is still a race condition which means your AddReceivedPacketToTextBox
can be called after the form is closed/disposed. The sequence would be something like:
I found out if I remove the event in a button event click , and close the form in that button click, everything is fine and my debugform gets closed without any exception...how odd!
That's not odd. Multithreading bugs ("Heisenbugs") are timing-related and small changes like that can affect the timing. But it's not a robust solution.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With