【问题标题】:Closing Form from inside an Invoke从调用内部关闭表单
【发布时间】:2015-04-28 10:32:23
【问题描述】:

像这样从Invoke 内部关闭表单:

Invoke(new Action(() => {
    Close();
    MessageBox.Show("closed in invoke from another thread");
    new Form1();
}));

表单一关闭就抛出异常:

在窗口之前不能在控件上调用 Invoke 或 BeginInvoke 句柄已创建。

但仅限于 NET 4.0。在 NET 4.5 上不会引发异常。

这是预期的行为吗?我该怎么办?

【问题讨论】:

    标签: c# winforms exception invoke ui-thread


    【解决方案1】:

    这是因为Close方法关闭了表单并销毁了它的句柄,然后在没有句柄的Closed表单中调用了MessageBox,所以出现了错误信息。

    我不明白您的目的,但是您应该将 Close 之后的代码移出调用,或者将 Close 移到它们之后。例如:

    Invoke(new Action(() => {
        Hide();
        MessageBox.Show("closed in invoke from another thread");
        new Form1();
        Close();
    }));
    

    编辑:

    关于Control.Invoke的MSDN注释:

    如果当前控件的底层窗口句柄尚不存在,则 Invoke 方法会向上搜索控件的父链,直到找到具有窗口句柄的控件或窗体。如果找不到合适的句柄,Invoke 方法将抛出异常。调用期间引发的异常将传播回调用者。

    【讨论】:

    • 你是对的。引发异常的是MessageBox。像这样MessageBox.Show((IWin32Window)null, "test"); 启动MessageBox,不再抛出异常。
    【解决方案2】:

    如果你在初始化期间启动一个线程,你不知道在另一个线程中初始化已经走了多远。

    您注意到不同 .Net 版本上的行为差异,但您无法确定不同机器上事物的顺序。

    我已经使用自己的消息泵、队列和普通的计时器控件解决了 Windows 窗体中的许多线程问题:

    • 为您的表单添加一个计时器控件,间隔很小(250 毫秒)
    • 向表单添加队列。
    • 让计时器事件将动作出列并执行。
    • 在初始化甚至其他后台作业期间将操作添加到队列中。

    使用这种方法会在初始化期间出现后台作业问题,但在关闭/处理表单期间也会出现问题,因为只有在表单功能齐全时才会触发计时器。

    【讨论】:

    • Invoke 由在表单创建后触发的事件调用。当 Close() 被执行时,表单被 100% 初始化。
    • @Chris,调用 Close 后,表单不再初始化,调用 Invoke 再次失败。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-16
    • 1970-01-01
    • 2019-06-28
    • 1970-01-01
    相关资源
    最近更新 更多