C# 调用表单时的ObjectDisposedException';s在没有';我没有被处置

C# 调用表单时的ObjectDisposedException';s在没有';我没有被处置,c#,winforms,invoke,C#,Winforms,Invoke,我们从一个尚未处理的表单上调用Invoke得到一个ObjectDisposedException。下面是一些演示问题的示例代码: 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(TaskW

我们从一个尚未处理的表单上调用
Invoke
得到一个
ObjectDisposedException
。下面是一些演示问题的示例代码:

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\u 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正在销毁句柄(当我可能重复使用此表单时)

  • 为什么我会得到一个ObjectDisposedException——它非常具有误导性(因为它没有被处置)。这就好像代码期望句柄只有在对象被释放时才会被销毁(这正是我所期望的)

  • 这应该是有效的吗?也就是说,是否应该允许我在ShowDialog之后调用控件

  • 笔记:
  • 执行第二个
    .ShowDialog()
    会创建一个新句柄

  • 如果在执行
    .ShowDialog()
    之后尝试执行
    调用
    ,则会出现InvalidOperationException,指出“在创建窗口句柄之前,无法对控件调用Invoke或BeginInvoke。”

  • 如果在执行
    .ShowDialog()
    之后,我访问
    句柄
    属性,然后执行
    调用
    ,则该操作将成功

  • 为什么ShowDialog正在销毁句柄(当我可能重复使用此表单时)

    销毁本机窗口并使其消失。在此之后,句柄属性将为IntPtr.Zero

    为什么我会得到一个ObjectDisposedException——它非常具有误导性(因为它没有被处置)

    是的,在对话的情况下会产生误导。编写此代码是为了处理使用Show()显示的表单的更常见情况。一旦本机窗口被销毁,它将处理挂起的调用队列,并将它们标记为完成。并将其“上次引发的异常”状态设置为ObjectDisposedException(引用源代码段中的entry.exception)。它明确地这样做是为了防止任何被调用的代码在本机窗口消失的情况下运行,这样的代码通常会随着ODE一起消失。它只是仓促行事,提前提出异常。您可以争辩说,InvalidOperationException更合适,但他们选择了ODE

    这应该是有效的吗?也就是说,是否应该允许我在ShowDialog之后调用控件

    不,那不行。句柄属性的值是确定要调用哪个线程所必需的。但在ShowDialog()返回后,它是IntPtr.Zero


    在这里您遇到了一个棘手的问题,但一般策略必须始终是确保在允许表单关闭之前完成或终止线程。更多信息请访问。

    感谢您的回答和链接。非常有用。我不明白的是,为什么需要破坏把手来隐藏窗口。如果我要连续执行几个ShowDialog,那么似乎没有必要销毁句柄。我错过什么了吗?我一直以为把手只有在处理后才会被破坏。我只是想,你对此无能为力。隐藏对话框也会破坏本机窗口。啊,我不知道。谢谢在查看一个类似的问题(关闭表单调用invoke)并尝试检查表单及其一个控件上的
    IsDisposed
    时,仍然得到
    invalidooperationexception
    ,我发现在这种情况下
    disposed
    是正确的,即使
    IsDisposed
    不是。