为什么在事件循环开始之前处理控件时不执行 BeginInvoke 中的操作?

Why isn't the action in BeginInvoke executed when a control is disposed before the event loop starts?

当 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 中的什么地方 enforced/implemented; WinForms 会在处理控件时从队列中删除消息吗?

(查看了 GitHub 中的源代码,但无法锻炼 where/how 这种情况发生了)

谢谢!

您要查找的代码is in 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 时,队列中的任何 window 消息都会被抽取。调用的回调通过 window 消息队列发送,因此所有回调都在您调用 DoEvents 时完成。所以当你Dispose之后,即使你调用EndInvoke也没有异常,因为回调已经完成了。