65.9K
CodeProject 正在变化。 阅读更多。
Home

关于 Windows 消息队列

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.67/5 (5投票s)

2002年4月18日

3分钟阅读

viewsIcon

174000

downloadIcon

1654

本文是为 Win32 API 程序员撰写的,主题是 Windows 消息队列。

Sample Image - queue.gif

引言

众所周知,MFC 是在 Windows 系统上开发应用程序的最强大工具。它的类专门用于处理你能想象到的各种复杂任务,并且所选解决方案的灵活性非常出色。 它是开发大型软件的最佳方法,可以使程序员远离各种细节问题。在处理基于窗口的系统时,这应该是一个最大的优势,但是...

线程

我们在这里谈论的不是基于窗口的系统。“程序的基本工作单元是线程”,MSDN 库中这样写道。除了堆栈和执行路径等许多背景细节之外,它还必须涵盖消息队列的实现。这并非强制规定,但您会在每个支持窗口的线程中找到它。 这也是一个非常普遍的误解的原因:窗口有一个消息队列。 错!消息队列属于线程,发送到窗口的消息由线程中的消息循环分发到该线程。我将通过这篇文章附带的程序来说明这一点。

程序

这是一个基于控制台的应用程序;无模式对话框通过控制台的消息队列管理其消息,因此一次不会有两个队列。该代码为您提供了一个类和主函数。

CConsoleQueue

此类实现消息循环和处理消息的原始系统。它具有以下方法

RunQueue - 运行经典的消息循环

  while (GetMessage(&msg, NULL, 0, 0))    // extract the message 
                                          // from the queue
  {
    if (fctMan=(*this)[msg.message])
      fctMan((void *)msg.wParam);
    else
    {
      TranslateMessage(&msg); // necessary to listen the keyboard
      DispatchMessage(&msg);  // message goes to the default 
                              // procedure
    }
  }

EndQueue - 通过发送 WM_QUIT 终止消息循环

  PostMessage(NULL, WM_QUIT, 0, 0);     // message to interrupt
                                        // the loop by returning
                                        // FALSE on GetMessage

RegisterMessageHandler - 将处理程序函数注册到特定消息。当消息在消息循环中被接收时,将调用该处理程序。要传递句柄的值,您应该在调用 PostMessage 时通过 WPARAM 传递它。

注意:这可以被认为是 The Adaptive Communication Environment (ACE) 提出的 reactor 概念的框架。

RemoveAll - 移除所有关联(来自 reactor)

此函数的目的是 - 逐步执行 - 如下

1. 创建一个用于向队列发送消息的无模式对话框

  hWndDialog=CreateDialog(NULL, MAKEINTRESOURCE(IDD_DIALOG_TEST),
    NULL, DialogProcedure); 
  ShowWindow(hWndDialog, SW_SHOW); 

2. 在线程上设置一个计时器

  nTimer=SetTimer(NULL, 0, nDelay*1000, TimerProcedure);

3. 注册用户定义消息的处理程序

  queue.RegisterMessageHandler(WM_USER_DEFINED, UserDefined);

4. 运行队列

  queue.RunQueue();

行为

目的是在消息队列中放入 WM_TIMER 消息;每个 WM_TIMER 都将打印到控制台的全局缓冲区。如果缓冲区在最后的 nKeepQueueSteps WM_TIMER 中没有修改,最后一个将停止计时器并结束消息队列。 修改缓冲区是在无模式对话框中通过在编辑框中写入内容并按“发送缓冲区”完成的。按“发送用户定义”会将 WM_USER_DEFINED 消息发送到队列。

最终

请注意,使用 NULL 作为第一个参数调用 PostMessage 会将消息放入当前线程的消息队列中。

另一个观察是关于设置计时器的。这只存在于队列的事件中,而不是窗口的资源。 它属于线程,并且只有通过调用 DispatchMessage 才能到达窗口。

最后要说的是消息循环。 这个表达式是程序员的一个限制。 实际上,您可以在单个线程的执行中拥有多个消息循环。 要测试这一点,只需将 main 的实际内容在函数的正文中像这样进行复制

  hWndDialog=CreateDialog(NULL, MAKEINTRESOURCE(IDD_DIALOG_TEST), 
    NULL, DialogProcedure); 
  // ...
  cout<<endl<<"program ends here..."<<endl;
  
  hWndDialog=CreateDialog(NULL, MAKEINTRESOURCE(IDD_DIALOG_TEST), 
    NULL, DialogProcedure); 
  // ...
  cout<<endl<<"program ends here..."<<endl;
  
  // ...  
  
  hWndDialog=CreateDialog(NULL, MAKEINTRESOURCE(IDD_DIALOG_TEST), 
    NULL, DialogProcedure); 
  // ...
  cout<<endl<<"program ends here..."<<endl;

© . All rights reserved.