为什么在事件循环开始之前处理控件时不执行 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
也没有异常,因为回调已经完成了。
当 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
也没有异常,因为回调已经完成了。