gpt4 book ai didi

c# - 未处理时调用 Form 的 Invoke 时出现 ObjectDisposedException

转载 作者:太空狗 更新时间:2023-10-30 01:22:57 25 4
gpt4 key购买 nike

我们从尚未处理的表单上调用 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(如下)期间创建的。 DestroyHandle 在完成时被 ShowDialog 调用。

来自 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();
}
}
}
}
// ...
}

问题:

  1. 为什么 ShowDialog 会破坏句柄(当我可能会重新使用此表单时)?

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

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

注意事项:

  1. 执行第二个 .ShowDialog() 会导致创建一个新句柄。

  2. 如果在执行 .ShowDialog() 之后我尝试执行 Invoke,我会收到一个 InvalidOperationException,指出“无法在控件上调用 Invoke 或 BeginInvoke直到创建了窗口句柄。”

  3. 如果在执行 .ShowDialog() 后我访问了 Handle 属性,然后执行 Invoke,它将成功。

最佳答案

Why is ShowDialog destroying the handle (when I might re-use this form)?

销毁 native 窗口并使其消失。此后,Handle 属性将为 IntPtr.Zero。

Why am I getting an ObjectDisposedException--its very misleading (since its not disposed).

是的,在对话的情况下会产生误导。编写代码是为了处理使用 Show() 显示的窗体的更常见情况。一旦 native 窗口被销毁,它就会处理未决调用队列并将它们标记为完成。并将它们的“最后引发的异常”状态设置为 ObjectDisposedException(引用源代码片段中的 entry.exception)。它明确地这样做是为了防止任何调用的代码在 native 窗口消失的情况下运行,这样的代码通常会随着 ODE 而死。它只是抢先一步并提早引发异常。您可能会争辩说 InvalidOperationException 会更合适,但他们选择了 ODE。

Should this be valid? That is, should I be allowed to Invoke on to a control after ShowDialog?

不,那行不通。需要 Handle 属性的值来确定调用哪个线程。但是在 ShowDialog() 返回后是 IntPtr.Zero。

您在这里遇到了一个极端情况,但一般策略必须始终是您确保在允许表单关闭之前线程已完成或终止。有关更多信息,请参阅 this answer .

关于c# - 未处理时调用 Form 的 Invoke 时出现 ObjectDisposedException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12428764/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com