ObjectDisposedException when invoke form call if it has not been deleted

We get an ObjectDisposedException from calling Invoke in a form that has not yet been deleted. Here is a sample code demonstrating the problem:

 public partial class Form2 : Form { void Form2_Load(object sender, EventArgs e) { // Start a task that does an Invoke on this control Task.Factory.StartNew(TaskWork); // Sleep here long enough to allow the task that does the Invoke // to execute to the point where it has: // a. Posted the message and // b. is waiting Thread.Sleep(500); // Cause ShowDialog to return by setting the DialogResult DialogResult = DialogResult.OK; } void TaskWork() { // This call doesn't return, but instead throws an ObjectDisposedException this.Invoke((MethodInvoker)(() => MessageBox.Show("Invoke succeeded"))); } } 

Here's the call code from Form1 (the main form), which I never close:

 public partial class Form1 : Form { Form2 m_form2 = new Form2(); void Form1_Load(object sender, EventArgs e) { // Call ShowDialog, but don't dispose it. m_form2.ShowDialog(); // Cause the finalizers to run. This causes an AggregateException to be thrown // due to the unhandled ObjectDisposedException from the Task. GC.Collect(); } } 

We dug up a Microsoft source and found that an exception was thrown during a call to DestroyHandle (below). DestroyHandle is called by ShowDialog upon completion.

From source .NET \ 4 \ DEVDIV_TFS \ Dev10 \ Releases \ RTMRel \ ndp \ fx \ src \ WinForms \ Managed \ System \ WinForms \ Control.cs \ 1305376 \ Control.cs:

 protected virtual void DestroyHandle() { // ... // If we're not recreating the handle, then any items in the thread callback list will // be orphaned. An orphaned item is bad, because it will cause the thread to never // wake up. So, we put exceptions into all these items and wake up all threads. // If we are recreating the handle, then we're fine because recreation will re-post // the thread callback message to the new handle for us. // if (!RecreatingHandle) { if (threadCallbackList != null) { lock (threadCallbackList) { Exception ex = new System.ObjectDisposedException(GetType().Name); while (threadCallbackList.Count > 0) { ThreadMethodEntry entry = (ThreadMethodEntry)threadCallbackList.Dequeue(); entry.exception = ex; entry.Complete(); } } } } // ... } 

Questions:

  • Why does ShowDialog destroy the handle (when can I reuse this form)?

  • Why I get an ObjectDisposedException - its very misleading (as it is not used). As if the code was expecting the descriptor to be destroyed only when the object was deleted (this is what I expected).

  • Should it be valid? That is, should I allow Invoke to the control after ShowDialog?

Notes:

  • Executing the second .ShowDialog() causes the creation of a new descriptor.

  • If, after executing .ShowDialog() I try to execute Invoke , I get an InvalidOperationException which states that "Invoke or BeginInvoke cannot be called in the control until the window handle is created."

  • If, after executing .ShowDialog() I access the Handle property and then execute Invoke , it will succeed.

+4
source share
1 answer

Why does ShowDialog destroy the handle (when can I reuse this form)?

To destroy your own window and make it disappear. After that, the Handle property will be IntPtr.Zero.

Why I get an ObjectDisposedException - its very misleading (as it is not located).

Yes, this is misleading in case of dialogue. The code was written to address a more common case of the form displayed using Show (). As soon as its own window is destroyed, it will work in the queue of waiting calls and mark their completion. And assigns the status of the "last exception" in ObjectDisposedException (entry.exception in the source code fragment). He does this explicitly to prohibit the launch of any running code with a damaged window, such code very often dies using ODE. He simply jumps with a pistol and throws an exception earlier. You could argue that an InvalidOperationException would be more appropriate, but they chose ODE.

Should it be valid? That is, should I allow Invoke to the control after ShowDialog?

No, that will not work. The value of the Handle property is required to determine which thread to access. But this is IntPtr.Zero after the return of ShowDialog ().

This is where you make the corner case, but the general strategy should always be to make sure the threads are completed or completed before allowing closure of the form. More on this in this answer .

+4
source

All Articles