1 / 140

第三章 驱动 MFC 应用程序的引擎: 消息映射

第三章 驱动 MFC 应用程序的引擎: 消息映射. 众所周知, Windows 应用程序的运行是 消息驱动 。了解和掌握 API 应用程序的运行控制 —— 消息循环 接收、分发消息, 窗口 函数 处理消息,直至 退出 消息结束 消息循环 导致进程结束的 运 行机制 是理解 消息驱动 机制最直观的方法;同时如何设计 窗口 函数 实现消息的处理也是把握 API 应用程序设计、编程的关键 基础之一。 使用 MFC 编写的面向对象 Windows 应用程序的运行控制在本

lucia
Download Presentation

第三章 驱动 MFC 应用程序的引擎: 消息映射

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第三章 驱动 MFC 应用程序的引擎: 消息映射

  2. 众所周知,Windows 应用程序的运行是消息驱动。了解和掌握 API 应用程序的运行控制 —— 消息循环接收、分发消息,窗口 函数处理消息,直至退出消息结束消息循环导致进程结束的运 行机制是理解消息驱动机制最直观的方法;同时如何设计窗口 函数实现消息的处理也是把握 API 应用程序设计、编程的关键 基础之一。 使用 MFC 编写的面向对象 Windows 应用程序的运行控制在本 质上与 API 应用程序是完全一致的。因此,掌握消息驱动机制的实现方法,如何实现消息的响应处理,从而控制进程、实现 程序设计需求也是在 VC 集成环境中使用 MFC 进行面向对象的 Windows 程序设计、编程的关键基础之一。

  3. 在 MFC 应用程序中看不到 API应用程序中实现消息驱动控制 的窗口函数 WndProc和函数中用于处理消息的switch-case 程序 结构,而是通过另一种方法实现的。这种方法是在每个需要响 应和处理消息的类(这些类必须是 CCmdTarget类的直接或间接 从派生类)添加用于处理消息的成员函数,并在这些成员函数 和被处理的消息之间添加映射表从而实现消息驱动机制的。 这种消息映射的方法更符合面向对象程序设计。该方法是如 何实现将消息映射到处理函数的呢?我们将通过对消息的类别 和结构、消息映射的工作原理、处理消息的路径等内容的学习 来解决这一问题。

  4. 3.1 消息类别及其描述 消息:应用程序中所发生事件的通知,即事件的信息化表示。 事件:用户对交互界面进行操作(例如操作鼠标、键盘、改变 窗口、选择菜单项等);或者程序的某种特定运行(窗 口的创建显示、窗口客户区中被显示数据修改引起的重 绘操作)都会引起事件的产生。 基于事件触发而运行的应用程序必须是完全面向消息处 理的,即始终按如下步骤循环: 等待消息接收消息分析消息产生操作 完成操作返回

  5. 消息的格式: typedef struct tagMSG { HWND hWnd; // 消息的目标窗口句柄 UINT message; // 消息的标识符 WPARAM wParam; // 32位消息附加参数 LPARAM lParam; // 32位消息附加参数 DWORD time; // 消息发至队列的时间 POINT pt; // 消息发出时的鼠标位置 } MSG; 其中前四个成员是描述消息的主要参数: ① hWnd:消息的目标窗口句柄,即消息的接收者。所谓句 柄是一个特殊整数,作为 Windows 各类资源的索引,是访 问资源的唯一依据。不同资源的句柄类型也不同:

  6. 窗口句柄 HWND, 进程实例句柄 HINSTANCE, 光标句柄 HCURSOR, 画笔句柄 HPEN, 画刷句柄 HBRUSH, 设备环境 句柄 HDC, 位图句柄 HBITMAP, 字体句柄 HFONT 等。 ② message:消息的标识符,即消息的名称。 ③ wParam 和 lParam:消息的附加参数,即为消息的响应函 数传递参数。 消息队列:为管理和处理应用程序运行中所产生的驱动消息, Windows 平台提供了一个系统消息队列,用于存放分发给所 有应用程序的消息; Windows 平台还为每一个进程建立一个 程序消息队列,用于接收发到该进程的所有消息,以便分发 到进程中的不同消息接收对象。

  7. 面向对象程序的消息驱动相当于面向过程程序的函数调用,面向对象程序的消息驱动相当于面向过程程序的函数调用, 但在调用目标、调用结果和参数传递三个方面存在不同: 调用目标: 函数调用:被调用的函数,由函数名确定。调用目标唯一。 消息驱动:能接收消息的对象,由消息的窗口句柄和消息标 识符确定。调用目标不唯一。 调用结果: 函数调用:被调用函数执行,并返回结果。调用无时延。 消息驱动:根据接收消息对象的状态决定消息对应的方法是 否执行,并返回响应状态。调用可能时延。

  8. 参数传递: 函数调用:以实参形式传递给被调用函数,作为执行条件。 消息驱动:只在消息标识符对应的对象方法被调用时,参数 才被传递,传递方式与函数调用类似。 根据消息的接收对象、消息名(标识符)和附加参数的不 同,Win32 的消息可以分为四类:窗口消息、命令消息、控件 消息和用户界面更新消息。

  9. 3.1.1 窗口消息 相关操作:与窗口相关的操作,例如,创建窗口、绘制窗口、 移动窗口、改变窗口尺寸、鼠标在窗口区域内的各种操作 等。CWnd类或其派生类对象都能够响应处理这类消息。 形式:WM_XXX,其中 WM为窗口消息类型前缀,XXX 表示具 体的窗口消息种类。例如: WM_CREATE The WM_CREATE message is sent when an application requests that a window be created by calling the CreateWindowEx or CreateWindow function. The window procedure of the new window receives this message after the window is created, but before the window becomes visible. The message is sent before the CreateWindowEx or CreateWindow function returns.

  10. WM_PAINT An application sends the WM_PAINT message when the system or another application makes a request to paint a portion of an application's window. The message is sent when the UpdateWindow or RedrawWindow function is called, or by the DispatchMessage function when the application obtains a WM_PAINT message by using theGetMessage or PeekMessage function. WM_LBUTTONDOWN The WM_LBUTTONDOWN message is posted when the user presses the left mouse button while the cursor is in the client area of a window. If the mouse is not captured, the message is posted to the window beneath the cursor. Otherwise, the message is posted to the window that has captured the mouse.

  11. WM_CLOSE The WM_CLOSE message is sent as a signal that a window or an application should terminate. 所有的窗口消息都可以在 MSDN中查询到。 主要参数: hWnd: 接收消息的窗口句柄 message:WM_XXX wParam:随 WM_XXX 而变 lParam:随 WM_XXX 而变 产生途径: ·窗口范围内的交互操作产生; ·系统框架产生的窗口消息; ·程序根据需要发送窗口消息。

  12. 3.1.2 命令消息 相关操作:处理用户的某个请求或者执行用户的某个命令。 CCmdTarget 类或其派生类的对象都能够处理这类消息。 形式:WM_COMMAND 主要参数: hWnd: 忽略 message:WM_COMMAND wParam:低 16 位为命令 ID 高 16 位为 0 lParam:0L 由于 wParam的高、低16 位表示不同的含义,需分解 wParam 来判断命令消息,也需要组装一个 wParam用于生成命令消 息。高、低字的分解和组合宏可以帮助完成这些工作。

  13. 参数分解: ·HIGHWORD(wParam) 分解获取高 16 位,如果为 0 表示该 消息为命令消息。 ·LOWWORD(wParam) 分解获取低 16 位,指示发出该命令 消息的资源 ID。例如:菜单命令消息的菜单项资源 ID; 工具栏按钮消息的按钮资源 ID 等。 参数组合: 使用宏 MAKEWPARAM可以将两个 16位字组合成一个 32位字,例如生成一条打开文件的命令消息:打开文件的菜单项 资源 ID 为 ID_FILE_OPEN,该命令消息为: (WM_COMMAND, MAKEWPARAM(ID_FILE_OPEN, 0), 0L)

  14. 产生途径: ·选择菜单项; ·单击工具栏按钮; ·按加速键; ·程序根据需要发送的命令消息。

  15. 3.1.3 命令的用户界面更新消息 相关操作:程序根据运行状态,在适当的时候更新某些命令的 用户界面(例如 菜单、工具栏等)的状态(例如,使能或禁用等)。当 CCmdTarget 派生类对象接收到这类消息后,则通 过 CCmdUI 类对象来修改相应命令的用户界面状态。 形式:WM_COMMAND 主要参数: hWnd: 忽略 message:WM_COMMAND wParam:低 16 位为命令 ID 高 16 位为 0 lParam:0L 产生途径: ·下拉菜单列表; ·单击工具栏按钮;

  16. 3.1.4 控件消息 相关操作:此类消息与控件窗口中发生的某个事件相关,例 如:文本框控件窗口内的内容发生改变、列表框控件中某个 选项被选择、按钮控件被单击、滑尺控件的游标被移动等, 都会发出相应的控件事件消息。 形式:控件的变化是近年来软件开发中最为活跃的,随着控件 的种类不断增加,控件消息的类别也迅速增加,原来的控件 消息格式越来越不能满足控件消息的描述,因此,必须寻求 新的控件消息描述方法。由于历史的原因,控件消息的新旧 描述形式必须兼容。三种不同的控件消息形式、主要字段的 内容分别描述如下:

  17. 1 第 1 种控件消息格式(仿窗口消息格式) 最早出现的控件消息形式,这种格式遵循窗口消息的格式 WM_XXX,因此可视为窗口消息集的一部分。 主要参数: hWnd: 接收消息的窗口句柄 message:WM_XXX wParam:随 WM_XXX 而变 lParam:随 WM_XXX 而变 消息标识中的 XXX 表示不同控件消息,例如:

  18. WM_HSCROLL The WM_HSCROLL message is sent to a window when a scroll event occurs in the window's standard horizontal scroll bar. This message is also sent to the owner of a horizontal scroll bar control when a scroll event occurs in the control. WM_VSCROLL The WM_VSCROLL message is sent to a window when a scroll event occurs in the window's standard vertical scroll bar. This message is also sent to the owner of a vertical scroll bar control when a scroll event occurs in the control.

  19. WM_PARENTNOTIFY TheWM_PARENTNOTIFY message is sent to the parent of a child window when the child window is created or destroyed, or when the user clicks a mouse button while the cursor is over the child window. When the child window is being created, the system sends WM_PARENTNOTIFY just before the CreateWindow or CreateWindowEx function that creates the window returns. When the child window is being destroyed, the system sends the message before any processing to destroy the window takes place.

  20. WM_CTLCOLOR Windows controls frequently send notification messages to their parent windows. For instance, many controls send a control color notification message (WM_CTLCOLOR or one of its variants) to their parent to allow the parent to supply a brush for painting the background of the control. WM_DRAWITEM The WM_DRAWITEM message is sent to the owner window of an owner-drawn button, combo box, list box, or menu when a visual aspect of the button, combo box, list box, or menu has changed.

  21. WM_DELETEITEM The WM_DELETEITEM message is sent to the owner of a list box or combo box when the list box or combo box is destroyed or when items are removed by the LB_DELETESTRING, LB_RESETCONTENT, CB_DELETESTRING, or CB_RESETCONTENT message. The system sends a WM_DELETEITEM message for each deleted item. The system sends the WM_DELETEITEM message for any deleted list box or combo box item with nonzero item data. WM_MEASUREITEM The WM_MEASUREITEM message is sent to the owner window of an owner-drawn button, combo box, list box, list view control, or menu item when the control or menu is created.

  22. WM_CHARTOITEM The WM_CHARTOITEM message is sent by a list box with the LBS_WANTKEYBOARDINPUT style to its owner in response to a WM_CHAR message.

  23. 2 第 2 种控件消息格式(仿命令消息格式) 这是继仿窗口消息之后出现的控件消息描述格式,它遵循 了命令消息格式,与命令消息的区别在于 wParam高16 位 为消息通知码,而不为 0;并且 lParam不为 0L,而是控件 窗口句柄。 主要参数: hWnd: 忽略 message:WM_COMMAND wParam:低16 位为命令 ID,高16 位为消息通知码 lParam:发出消息的控件的窗口句柄

  24. 参数分解: ·HIGHWORD(wParam) 获取消息通知码 XN_XXX。 其中前缀 XN 表示某类控件的通知消息。例如: EN_CHANGE表示文本框控件窗口内容变化; BN_CLICKED表示按钮控件被单击。 ·LOWWORD(wParam) 获取控件的 ID。 参数组合:例如生成对话框OK 按钮的单击事件消息: 首先获取OK 按钮的窗口句柄: HWND hwnd = GetDlgItem(IDOK)->GetSafeHwnd(); 发出的消息为: (WM_COMMAND, MAKEWPARAM(IDOK, BN_CLICKED), hwnd)

  25. 3 第 3 种控件消息格式(控件通知消息格式) 这是一种在 Win32 中新增加的适用于所有控件的事件通知 消息的描述格式。这种消息描述格式是通过增加一个消息 类别 WM_NOTIFY,表示由控件发出的事件通知;附加参数 wParam 被设置为控件的 ID,而参数 lParam指向一个描述 通知消息的数据结构NMHDR变量。这种格式可以满足日益 复杂的控件消息的需要。这是最流行的,具有很强描述能 力的消息描述格式。 主要参数: hWnd: 忽略 message:WM_NOTIFY wParam:低16 位发出消息的控件的 ID,高16 位为 0 lParam:描述消息的结构变量地址

  26. 其中:lParam所指向的数据结构分两种情况: ·对于一些通用的事件通知消息(消息名前缀:NM)和 工具提示控件的通知消息 TTN_SHOW和 TTN_POP 等, lParam 指向数据结构 NMHDR,该数据结构定义如下: typedef struct tagNMHDR {    HWND hwndFrom; // 发出消息的控件窗口的句柄。UINT idFrom; // 发出消息的控件ID。UINT code; // 通知码,例如工具栏提示显示消// 息的通知码应为 TTN_SHOW。 } NMHDR; 所有新增的控件都需要使用的通知消息码包括:

  27. ·对于大多数情况,lParam可以指向一个比 NMHDR更 大的扩展数据结构,以便通过该结构变量传递更多的 需要扩展的消息信息,这类数据结构的第一个成员必 须是 NMHDR类型成员。例如,在列表视图控件中按 下键盘按键的通知消息被命名为 LVN_KEYDOWN,该 消息的 lParam指向名为 LV_KEYDOWN的数据结构变 量,该结构定义如下: typedef struct tagLV_KEYDOWN {    NMHDR hdr;   // 该结构成员中的通知码成员 // hdr.code =LVN_KEYDOWN WORD wVKey;  // 虚拟的键盘按键码 UINT flags;  // 该值必须总为 0 } LV_KEYDOWN;

  28. 产生途径: ·由控件的各类事件发出; ·程序根据需要模拟控件事件发送通知消息。

  29. 3.2 MFC 消息映射原理 3.2.1 应用程序类的 Run() 函数 MFC 应用程序中对消息的处理分为两个阶段: ·第一阶段:应用程序类的成员函数 Run 从消息队列中获取消 息,并将消息发送到相应的目标类对象 。 ·第二阶段:在 MFC 消息机制的导引下,在目标类对象中为所 有消息寻找响应消息的处理函数。这些能响应消息的目标类 可以是 CCmdTarget的任何派生类,例如主窗口类、视图类、 文档类对象等。 应用程序类的 Run是一个虚函数,但在一般情况下,如果没 有特殊要求,不需要在 CXXXApp 类中进行重新定义,因此实际 上运行的是其基类 CWinApp对象的 Run 函数版本,它的定义代 码可在系统源文件 “Appcore.cpp”中查询到。

  30. // Main running routine until application exits int CWinApp::Run() { if (m_pMainWnd == NULL && AfxOleGetUserCtrl()) {// Not launched /Embedding or /Automation, but has no main window! TRACE0("Warning: m_pMainWnd is NULL in CWinApp::Run - quitting application.\n"); AfxPostQuitMessage(0); } return CWinThread::Run(); } 分析代码,不难看出:只要 m_pMainWnd 指向的主窗口对象 被成功创建,或用户不在应用程序的控制之下,则 CWinApp基 类 CWinThread 的 Run函数版本就被调用。该函数版本的代码可 在系统源文件 “Thrdcore.cpp” 中查询到。

  31. int CWinThread::Run() { ASSERT_VALID(this); // 判断线程是否合法 // for tracking the idle time state BOOL bIdle = TRUE; LONG lIdleCount = 0; // acquire and dispatch messages until a WM_QUIT message is received. for (;;) // 消息循环入口 { // phase1: check to see if we can do idle work (空闲处理循环) while (bIdle && !::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)) { // call OnIdle while in bIdle state if (!OnIdle(lIdleCount++)) bIdle = FALSE; // assume "no idle" state }

  32. // phase2: pump messages while available(各类消息的获取和分发) do { // pump message, but quit on WM_QUIT if (!PumpMessage()) return ExitInstance(); //进程结束退出 // reset "no idle" state after pumping "normal" message if (IsIdleMessage(&m_msgCur)) { bIdle = TRUE; lIdleCount = 0; } } while ( ::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE) ); } ASSERT(FALSE); // not reachable }

  33. 完成消息的获取和分发操作的核心功能函数是 CWinThread的 成员函数 PumpMessage,它是一个虚函数,因此在 CWinThread 的派生类中可以重新定义该函数,但由于它是底层功能函数, 在没有必须的特殊底层操作,一般无须在派生类中重新定义, 其源代码可以在系统源文件 “Thrdcore.cpp” 中查询到,如下所示:

  34. BOOL CWinThread::PumpMessage() { ASSERT_VALID(this); if (!::GetMessage(&m_msgCur, NULL, NULL, NULL)) { #ifdef _DEBUG if (afxTraceFlags & traceAppMsg) TRACE0("CWinThread::PumpMessage - Received WM_QUIT.\n"); m_nDisablePumpCount++; // application must die // Note: prevents calling message loop things in 'ExitInstance' // will never be decremented #endif return FALSE; // 接收到退出消息 WM_QUIT }

  35. #ifdef _DEBUG if (m_nDisablePumpCount != 0) { TRACE0( "Error: CWinThread::PumpMessage called when not permitted.\n" ); ASSERT(FALSE); } #endif #ifdef _DEBUG if (afxTraceFlags & traceAppMsg) _AfxTraceMsg(_T("PumpMessage"), &m_msgCur); #endif

  36. // process this message if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur)) { ::TranslateMessage(&m_msgCur); // 消息预处理 ::DispatchMessage(&m_msgCur); // 消息分发 } return TRUE; } CWinThread::Run中实现消息获取和分发的另一个辅助操作是 CWinThread 的成员函数 IsIdleMessage完成的,它的功能是检测 不会引起“空闲”操作的消息,常用于检测鼠标在原位置发出的 鼠标移动事件消息。该函数是一个虚函数,但一般无须在派生 类中重定义,其代码可在系统源文件 “Thrdcore.cpp” 中查询到。

  37. BOOL CWinThread::IsIdleMessage(MSG* pMsg) { if ( pMsg->message == WM_MOUSEMOVE || pMsg->message == WM_NCMOUSEMOVE) { // mouse move at same position as last mouse move? if ( m_ptCursorLast == pMsg->pt && pMsg->message == m_nMsgLast ) return FALSE; m_ptCursorLast = pMsg->pt; // remember for next time m_nMsgLast = pMsg->message; return TRUE; } // WM_PAINT and WM_SYSTIMER (caret blink) return pMsg->message != WM_PAINT && pMsg->message != 0x0118; }

  38. 执行进程结束退出的成员函数 ExitInstance是结束 Run函数执 行导致跳出消息循环的唯一操作。该函数也是一个虚函数,但 一般情况下,也无须在派生类中重定义 ExitInstance,其代码可 在系统源文件 “Thrdcore.cpp” 中查询到。 int CWinThread::ExitInstance() { ASSERT_VALID(this); ASSERT(AfxGetApp() != this); int nResult = m_msgCur.wParam; // returns the value from PostQuitMessage return nResult; } 归纳上述分析,CWinThread::Run的运行流程如下图所示:

  39. 循环接收消息入口 for(;;) NO 消息队列为空? while (bIdle && !::PeekMessage(&m_msgCur, NULL,NULL,NULL, PM_NOREMOVE)) YES YES 连续空闲处理? if (!OnIdle(lIdleCount++)) bIdle = FALSE; NO PumpMessage(): 消息处理 从消息队列获取消息 GetMessage(&m_msgCur,NULL,NULL,NULL) 消息预翻译处理 PreTranslateMessage(&m_msgCur) 将虚键码翻译成字符消息并发送到消息队列中 ::TranslateMessage(&m_msgCur); ::DispatchMessage(&m_msgCur); 发送消息到窗口函数 YES 退出进程 return ExitInstance(); NO YES bIdle=TRUE; lIdleCount=0; 初始化空闲状态 NO 消息队列出现 新的消息? YES • while(::PeekMessage(&m_msgCur,NULL, • NULL, NULL, PM_NOREMOVE)); NO 开始 WM_QUIT? 结束 需要空闲处理?

  40. 3.2.2 SendMessage和 PostMessage函数 把消息发给各类窗口对象的方法除了在 Run 函数中调用的 ::DispatchMessage外,还可调用 SendMessage和 PostMessage 函数 在程序的任何位置向指定窗口对象发送一条确定消息。 1SendMessage 和 PostMessage 函数的区别 ⑴SendMessage用于向指定窗口发送确定消息,导致接收该 消息的窗口函数被调用,并等待窗口函数处理完该消息,返 回一个操作结果标志后,进程继续。 ⑵PostMessage用于向指定窗口寄送确定消息,就是把消息发 送到进程的消息队列中,并返回消息队列接收消息的标志, 而不等待消息的处理结果。进程通过消息循环 Run函数获取 发送到消息队列中的消息,并把它发送到指定窗口进行相应 的处理。

  41. 归纳发送消息和寄送消息的区别如下:

  42. 2 在 MFC 应用程序中如何发送和寄送消息 ⑴ 首先要获取接收消息的目标窗口对象(例如 wnd)或指向窗 口对象的指针(例如 pWnd); ⑵ 使用所获取的窗口对象或指向窗口对象的指针调用 CWnd 类 的成员函数 SendMessage或 PostMessage。例如: int res = wnd.SendMessage( message, wParam, lParam ); int res = wnd.PostMessage( message, wParam, lParam ); 或 int res = pWnd->SendMessage( message, wParam, lParam ); int res = pWnd->PostMessage( message, wParam, lParam );

  43. 3.2.3 如何检索消息队列中的消息 需要从消息队列中检索消息并处理时,可调用 PeekMessage 和 GetMessage函数,这两个函数的原型分别如下: BOOL PeekMessage( LPMSG lpMsg, // pointer to structure for message HWND hWnd, // handle to window UINT wMsgFilterMin, // first message UINT wMsgFilterMax, // last message UINT wRemoveMsg// removal flags );

  44. 功能: ·窥视和查询一下消息队列,如果消息队列中有指定范围 [wMsgFilterMin, wMsgFilterMax]内的消息,则获取一个消息保存 在 lpMsg 指向的消息结构变量中。 ·被获取的消息是否从消息队列中删除,取决于删除标志 wRemoveMsg (= PM_REMOVE,删除;= PM_NOREMOVE,不 删除)。 ·如果消息队列有可获取的消息,则返回非0,否则返回0,即 该函数的调用不阻塞当前线程运行。

  45. BOOL GetMessage( LPMSG lpMsg, // address of structure with message HWND hWnd, // handle of window UINT wMsgFilterMin, // first message UINT wMsgFilterMax// last message ); 功能: ·如果消息队列中有指定范围 [wMsgFilterMin, wMsgFilterMax] 内的 消息,则获取一个消息保存在 lpMsg 指向的消息结构变量中 并从队列中删除该消息后返回。 ·如果当前队列中无可获取的消息,则一直等待消息到达,该 函数所在的线程的运行被阻塞,直到新消息到达。

  46. 3.2.4消息映射表 消息被分发到目标对象后,如何与对应的功能匹配呢? ⑴在 Win32API应用程序中,这种匹配是通过接收到消息的窗口 函数中的 switch-case结构实现的。 ⑵ 在 MFC框架应用程序中,这种匹配的实现方法 ① 既不是 switch-case结构,因为不符合面向对象的设计。 ② 也不是虚函数,因为消息数量非常多,并且还会随着操作 平台的发展而增多。如果将每个消息响应函数定义为虚函 数,则会使每个能够处理消息的类对象都需要一个结构庞 大的虚函数表 vtab。这对于同一类的多个对象是冗余的, 况且一个特定的类并不需要响应所有的消息。显然从程序 的空间复杂度上考虑使用虚函数技术是不合理的。

  47. ③而是消息映射,即在每个能接收和处理消息的类中,定义③而是消息映射,即在每个能接收和处理消息的类中,定义 一个静态对照表—— 消息映射表,该表中只需要包含所属 类需要处理的消息和对应的消息处理函数的映射信息。进 程只要搜索该表就可以完成: ·判断能否响应所收到的消息; ·如果能响应,立即找到相应的消息处理函数。 由于消息映射表是通过一对宏增加到能够处理消息的类中 的,这意味着能够根据需要,非常方便地确定所有能处理 消息的类是否处理消息和处理哪些消息。 以一个 Hello应用程序的主窗口类 CMainFrame为例,说明 如何在类中增加消息映射表:

  48. 在类定义文件中增加消息映射定义宏: class CMainFrame : public CFrameWnd { … DECLARE_MESSAGE_MAP() }; 在类实现文件中增加消息映射实现宏: … BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) WM_PAINT ON_COMMAND(ID_MY_ITEM, OnMyItem) END_MESSAGE_MAP()

  49. 1代码宏 DECLARE_MESSAGE_MAP 定义了是什麽 #define DECLARE_MESSAGE_MAP()\ private:\ static const AFX_MSGMAP_ENTRY_messageEntries[ ];\ protected:\ static const AFX_MSGMAPmessageMap;\ static const AFX_MSGMAP* PASCAL _GetBaseMessageMap();\ virtual const AFX_MSGMAP* GetMessageMap() const;\ 其中数据类型 AFX_MSGMAP描述一个消息映射表,其定义:

More Related