2011-11-19 109 views
0

我一直在尝试理解事件循环(一般不太好),并且我已经读过Windows消息循环是单线程的。如果是这样,Application.DoEvents如何工作?事件循环没有一次处理一条消息,并且在处理每个消息/事件时阻塞?消息事件循环是否需要存在于与处理Application.DoEvents消息的消息不同的线程上?如果有独立的线程,我们称之为“主”线程的是哪一个?我相信我错过了一些非常简单的东西,我只是不知道它是什么。是Application.DoEvents发送消息到一个单独的线程?

回答

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中)EventHandler的控件和执行引发事件。我感到困惑的是,如果调用堆栈是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); 
     } 
    } 
} 

所以的DoEvents()实际上启动另一个循环来处理事件,而在最初的消息循环的DispatchMessage函数()内! 关键的区别在于,不使用GetMessage(),直到队列中有消息时才阻塞,我们使用PeekMessage()返回0,并在队列中不再有任何消息时存在循环。

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

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