为什么在事件循环启动之前释放控件时不执行 BeginInvoke 中的操作?



当Application.DoEvents运行时,WinForms(NET Core和Framework(中的以下代码不会尝试BeginInvoke中的操作:

namespace BeginInvokeTest
{
internal static class Program
{
/// <summary>
///  The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
ApplicationConfiguration.Initialize();
bool executed;
var form = new Form();
form.Show();
form.BeginInvoke(() => { executed = true; });
form.Dispose();
Application.DoEvents();
}
}
}

但是,如果在Application.DoEvents之后移动form.Dispose,则执行该操作。因此,我知道,如果在启动消息循环之前释放了拥有的控件,则BeginInvoke排队的消息将被忽略。

有人知道在WinForms中,这种行为是在哪里强制执行的吗;当控件被释放时,WinForms是否会从队列中删除消息?

(已经查看了GitHub中的源代码,但无法确定这是在哪里/如何发生的(

谢谢!

您要查找的代码在Control.DestoryHandle中,它由Control.Dispose调用

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

正如您所看到的,如果没有重新创建句柄,那么所有尚未调度的剩余回调都将设置为具有异常。因为这是异步的,所以除非调用EndInvoke,否则不会看到异常。

如果回调已经完成,您也不会看到任何异常。当您调用DoEvents时,队列中的任何窗口消息都会被泵送。调用的回调是通过窗口消息队列调度的,因此所有回调都在调用DoEvents时完成。因此,当您稍后调用Dispose时,即使您调用EndInvoke也没有异常,因为回调已经完成。

最新更新