我们得到一个ObjectDisposedException
从一个电话到Invoke
在尚未处理的表格上。这是一些演示该问题的示例代码:
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")));
}
}
这是来自 Form1(主窗体)的调用代码,我从未关闭它:
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();
}
}
我们深入研究了 Microsoft 源代码,发现异常是在调用 DestroyHandle 期间创建的(如下)。 ShowDialog 在完成时正在调用 DestroyHandle。
来自源.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();
}
}
}
}
// ...
}
问题:
为什么 ShowDialog 会破坏句柄(当我可能重新使用此表单时)?
为什么我会收到 ObjectDisposeException——它非常具有误导性(因为它没有被释放)。就好像代码期望只有在对象被处置时句柄才会被销毁(这正是我所期望的)。
这应该有效吗?也就是说,是否应该允许我在 ShowDialog 之后调用控件?
Notes:
做第二个.ShowDialog()
导致创建一个新句柄。
如果做完之后.ShowDialog()
我尝试做一个Invoke
,我收到一个 InvalidOperationException 异常,指出“在创建窗口句柄之前,无法在控件上调用 Invoke 或 BeginInvoke”。
如果做完之后.ShowDialog()
我访问Handle
属性,然后做一个Invoke
,就会成功。