Application.DoEvents是否将消息发送到单独的线程?

时间:2011-11-19 22:12:48

标签: user-interface windows-messages event-loop

我一直在尝试理解事件循环(不太顺利),我已经读过Windows消息循环是单线程的。如果是,Application.DoEvents如何工作?事件循环不会一次处理一条消息并在处理每条消息/事件时阻塞吗?消息事件循环是否需要存在于与处理Application.DoEvents的消息的线程不同的线程上?如果有单独的线程,那么我们称之为“主”线程?我确定我错过了很简单的东西,我只是不知道它是什么。

1 个答案:

答案 0 :(得分:0)

我花了一天的时间来解决这个问题(如果我说错了,请评论并告诉我,以便我能纠正它)。我实际上必须构建一个旧的Win32应用程序并自己创建消息循环(我是一个非常持久的SOB)。所以有一个名为WinMain的函数启动消息循环,如下所示:

while (GetMessage(&msg, NULL, 0, 0))
{
    if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

关于GetMessage()的事情是它会阻塞,直到消息队列中有消息可用。如果您运行Windowed应用程序并且只是坐在那里看窗口(不要导致任何将消息发送到队列的操作),则主要线程(创建窗口的线程)在GetMessage()上暂停。现在当消息发布时,我们进入while循环(即如果消息不是退出,则为0)。 DispatchMessage()是这里有趣的功能。此函数最终将导致(在.NET中)事件由控件和EventHandlers的执行引发。令我困惑的是,如果调用堆栈是GetMessage()/ DispatchMessage()/.../ EventHandler,Application.DoEvents()如何处理消息?嗯,这很简单。 Win32中的DoEvents看起来像这样:

void DoEvents()
{
   MSG msg;
   HACCEL hAccelTable;

   hAccelTable = LoadAccelerators(hInst, MAKEINTRESOURCE(IDC_TESTWIN32));

   // Main message loop:
   while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE) != 0)
   {
       if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
       {
           TranslateMessage(&msg);
           DispatchMessage(&msg);
       }
   }
}

因此,在初始消息循环的DispatchMessage()中,DoEvents()实际上启动了另一个循环来处理事件! 关键的区别在于,在队列中有消息之前,不使用阻塞的GetMessage(),而是使用PeekMessage()返回0并在队列中不再有任何消息时存在循环。

那么如果我们点击一​​个按钮两次并在该按钮的EventHandler中我们有一个DoEvents()调用呢?初始事件循环将处理第一次单击并触发事件。当EventHandler正在执行时,在DoEvents()调用时,事件将被第二次触发,并且将再次输入EventHandler(类似于递归调用)。那太吓人了!

所以最后,一切都发生在一个线程中,DoEvents()实际上会阻塞,直到所有消息都被处理然后返回。现在我要睡几天了。