MFC消息响应机制

【问题描述】MFC是C++的经典框架,基于消息响应机制。网上介绍MFC消息响应的文章很多。而我认为,是否理解MFC,有一点很重要,那就是看能否脱离界面编辑器,编写对话框的代码。本文介绍两个方面:

(1)如何利用代码编写对话框;

(2)消息映射如何实现。

【解析】

1 利用代码编写对话框

先看代码:

main.cpp

[html]  view plain copy
  1. #include "stdafx.h"  
  2. #include "resource.h"  
  3. #include "tchar.h"  
  4.   
  5. #define MAX_LOADSTRING 100  
  6.   
  7. // Global Variables:  
  8. HINSTANCE hInst;                                // current instance  
  9. TCHAR szTitle[MAX_LOADSTRING];                              // The title bar text  
  10. TCHAR szWindowClass[MAX_LOADSTRING];                                // The title bar text  
  11.   
  12. // Foward declarations of functions included in this code module:  
  13. ATOM                MyRegisterClass(HINSTANCE hInstance);  
  14. BOOL                InitInstance(HINSTANCE, int);  
  15. LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);  
  16. LRESULT CALLBACK    About(HWND, UINT, WPARAM, LPARAM);  
  17.   
  18. int APIENTRY WinMain(HINSTANCE hInstance,  
  19.                      HINSTANCE hPrevInstance,  
  20.                      LPSTR     lpCmdLine,  
  21.                      int       nCmdShow)  
  22. {  
  23.     // TODO: Place code here.  
  24.     MSG msg;  
  25.     HACCEL hAccelTable;  
  26.       
  27.     // Initialize global strings  
  28.     LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);  
  29.     LoadString(hInstance, IDC_BTNTEST, szWindowClass, MAX_LOADSTRING);  
  30.     MyRegisterClass(hInstance);  
  31.       
  32.     // Perform application initialization:  
  33.     if (!InitInstance (hInstance, nCmdShow))   
  34.     {  
  35.         return FALSE;  
  36.     }  
  37.       
  38.     hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_BTNTEST);  
  39.       
  40.     // Main message loop:  
  41.     while (GetMessage(&msg, NULL, 0, 0))   
  42.     {  
  43.         if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))   
  44.         {  
  45.             TranslateMessage(&msg);  
  46.             DispatchMessage(&msg);  
  47.         }  
  48.     }  
  49.       
  50.     return msg.wParam;  
  51. }  
  52.   
  53.   
  54.   
  55. //  
  56. //  FUNCTION: MyRegisterClass()  
  57. //  
  58. //  PURPOSE: Registers the window class.  
  59. //  
  60. //  COMMENTS:  
  61. //  
  62. //    This function and its usage is only necessary if you want this code  
  63. //    to be compatible with Win32 systems prior to the 'RegisterClassEx'  
  64. //    function that was added to Windows 95. It is important to call this function  
  65. //    so that the application will get 'well formed' small icons associated  
  66. //    with it.  
  67. //  
  68. ATOM MyRegisterClass(HINSTANCE hInstance)  
  69. {  
  70.     WNDCLASSEX wcex;  
  71.       
  72.     wcex.cbSize = sizeof(WNDCLASSEX);   
  73.       
  74.     wcex.style          = CS_HREDRAW | CS_VREDRAW;  
  75.     wcex.lpfnWndProc    = (WNDPROC)WndProc;  
  76.     wcex.cbClsExtra     = 0;  
  77.     wcex.cbWndExtra     = 0;  
  78.     wcex.hInstance      = hInstance;  
  79.     wcex.hIcon          = LoadIcon(hInstance, (LPCTSTR)IDI_BTNTEST);  
  80.     wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);  
  81.     wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);  
  82.     wcex.lpszMenuName   = (LPCSTR)IDC_BTNTEST;  
  83.     wcex.lpszClassName  = szWindowClass;  
  84.     wcex.hIconSm        = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);  
  85.       
  86.     return RegisterClassEx(&wcex);  
  87. }  
  88.   
  89. //  
  90. //   FUNCTION: InitInstance(HANDLE, int)  
  91. //  
  92. //   PURPOSE: Saves instance handle and creates main window  
  93. //  
  94. //   COMMENTS:  
  95. //  
  96. //        In this function, we save the instance handle in a global variable and  
  97. //        create and display the main program window.  
  98. //  
  99. BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)  
  100. {  
  101.     HWND hWnd;  
  102.       
  103.     hInst = hInstance; // Store instance handle in our global variable  
  104.       
  105.     //menu  
  106.     static HMENU hMenu,hMenuPop1,hMenuPop2;  
  107.     hMenu = CreateMenu();  
  108.       
  109.     hMenuPop1 = CreateMenu();  
  110.     hMenuPop2 = CreateMenu();  
  111.     AppendMenu(hMenuPop1, MF_STRING, IDM_TEST, "&测试");  
  112.     AppendMenu(hMenuPop2, MF_STRING, IDM_ABOUT, "&关于");  
  113.     AppendMenu(hMenuPop2, MF_STRING, IDM_EXIT, "&退出");  
  114.     AppendMenu(hMenu, MF_POPUP, (UINT)hMenuPop1, "&菜单1");  
  115.     AppendMenu(hMenu, MF_POPUP, (UINT)hMenuPop2, "&菜单2");  
  116.       
  117.       
  118.     hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,  
  119.         CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, hMenu, hInstance, NULL);  
  120.       
  121.     if (!hWnd)  
  122.     {  
  123.         return FALSE;  
  124.     }  
  125.       
  126.       
  127.     ShowWindow(hWnd, nCmdShow);  
  128.     UpdateWindow(hWnd);  
  129.       
  130.     return TRUE;  
  131. }  
  132.   
  133. //  
  134. //  FUNCTION: WndProc(HWND, unsigned, WORD, LONG)  
  135. //  
  136. //  PURPOSE:  Processes messages for the main window.  
  137. //  
  138. //  WM_COMMAND  - process the application menu  
  139. //  WM_PAINT    - Paint the main window  
  140. //  WM_DESTROY  - post a quit message and return  
  141. //  
  142. //  
  143. #define btn1 1  
  144. #define btn2 2  
  145. LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)  
  146. {  
  147.     int wmId, wmEvent;  
  148.     PAINTSTRUCT ps;  
  149.     HDC hdc;  
  150.     TCHAR szHello[MAX_LOADSTRING];  
  151.     LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);  
  152.       
  153.     switch (message)   
  154.     {  
  155.     case WM_CREATE:  
  156.         {  
  157.             //button  
  158.             HWND hButton1 = CreateWindow(_T("button"), _T("Btn1"),  
  159.                 WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON, 100, 100, 100, 30, hWnd, (HMENU)btn1, hInst, NULL);  
  160.             HWND hButton2 = CreateWindow(_T("button"), _T("Btn2"),  
  161.                 WS_CHILD|WS_VISIBLE|BS_BITMAP, 100, 200, 100, 30, hWnd, (HMENU)btn2, hInst, NULL);  
  162.             HBITMAP hbmp=LoadBitmap(hInst,MAKEINTRESOURCE(IDB_Btn2));  
  163.             SendMessage(hButton2, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM) hbmp);  
  164.         }  
  165.         break;  
  166.     case WM_COMMAND:  
  167.         wmId    = LOWORD(wParam);   
  168.         wmEvent = HIWORD(wParam);   
  169.         // Parse the menu selections:  
  170.         switch (wmId)  
  171.         {  
  172.         case btn1:  
  173.             MessageBox(hWnd, _T("Button1 press!"), _T("Message"), MB_OK|MB_ICONINFORMATION);  
  174.             break;  
  175.         case btn2:  
  176.             MessageBox(hWnd, _T("Button2,press!"), _T("Message"), MB_OK|MB_ICONINFORMATION);  
  177.             break;  
  178.         case IDM_TEST:  
  179.             MessageBox(hWnd, _T("菜单测试"), _T("菜单测试"), MB_OK|MB_ICONINFORMATION);  
  180.             break;  
  181.         case IDM_ABOUT:  
  182.             DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);  
  183.             break;  
  184.         case IDM_EXIT:  
  185.             DestroyWindow(hWnd);  
  186.             break;  
  187.         default:  
  188.             return DefWindowProc(hWnd, message, wParam, lParam);  
  189.         }  
  190.         break;  
  191.         case WM_PAINT:  
  192.             hdc = BeginPaint(hWnd, &ps);  
  193.             // TODO: Add any drawing code here...  
  194.             RECT rt;  
  195.             GetClientRect(hWnd, &rt);  
  196.             DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);  
  197.             EndPaint(hWnd, &ps);  
  198.             break;  
  199.         case WM_DESTROY:  
  200.             PostQuitMessage(0);  
  201.             break;  
  202.         default:  
  203.             return DefWindowProc(hWnd, message, wParam, lParam);  
  204.     }  
  205.     return 0;  
  206. }  
  207.   
  208. // Mesage handler for about box.  
  209. LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)  
  210. {  
  211.     switch (message)  
  212.     {  
  213.     case WM_INITDIALOG:  
  214.         return TRUE;  
  215.           
  216.     case WM_COMMAND:  
  217.         if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)   
  218.         {  
  219.             EndDialog(hDlg, LOWORD(wParam));  
  220.             return TRUE;  
  221.         }  
  222.         break;  
  223.     }  
  224.     return FALSE;  
  225. }  


resource.h

[html]  view plain copy
  1. //{{NO_DEPENDENCIES}}  
  2. // Microsoft Developer Studio generated include file.  
  3. // Used by Test2.rc  
  4. //  
  5. #define IDS_APP_TITLE                   1  
  6. #define IDC_BTNTEST                     2  
  7. #define IDS_HELLO                       3  
  8. #define IDI_BTNTEST                     101  
  9. #define IDI_SMALL                       102  
  10. #define IDD_ABOUTBOX                    103  
  11. #define IDR_MENU1                       110  
  12. #define IDB_Btn2                        201  
  13. #define IDM_ABOUT                       1001  
  14. #define IDM_EXIT                        1002  
  15. #define IDM_TEST                        1003  
  16.   
  17. // Next default values for new objects  
  18. //   
  19. #ifdef APSTUDIO_INVOKED  
  20. #ifndef APSTUDIO_READONLY_SYMBOLS  
  21. #define _APS_NEXT_RESOURCE_VALUE        113  
  22. #define _APS_NEXT_COMMAND_VALUE         40001  
  23. #define _APS_NEXT_CONTROL_VALUE         1000  
  24. #define _APS_NEXT_SYMED_VALUE           101  
  25. #endif  
  26. #endif  

实现步骤如下所述:
(1)设计窗口类

[html]  view plain copy
  1. WNDCLASSEX wcex;  
  2.       
  3.     wcex.cbSize = sizeof(WNDCLASSEX);   
  4.       
  5.     wcex.style          = CS_HREDRAW | CS_VREDRAW;  
  6.     wcex.lpfnWndProc    = (WNDPROC)WndProc;  
  7.     wcex.cbClsExtra     = 0;  
  8.     wcex.cbWndExtra     = 0;  
  9.     wcex.hInstance      = hInstance;  
  10.     wcex.hIcon          = LoadIcon(hInstance, (LPCTSTR)IDI_BTNTEST);  
  11.     wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);  
  12.     wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);  
  13.     wcex.lpszMenuName   = (LPCSTR)IDC_BTNTEST;  
  14.     wcex.lpszClassName  = szWindowClass;  
  15.     wcex.hIconSm        = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);  

 最为重要的一行代码:

[html]  view plain copy
  1. wcex.lpfnWndProc    = (WNDPROC)WndProc;  

该行代码传递了消息响应处理函数。

(2)注册窗口类

[html]  view plain copy
  1. return RegisterClassEx(&wcex);  


(3) 添加菜单

[html]  view plain copy
  1. static HMENU hMenu,hMenuPop1,hMenuPop2;  
  2. hMenu = CreateMenu();  
  3.       
  4. hMenuPop1 = CreateMenu();  
  5. hMenuPop2 = CreateMenu();  
  6. AppendMenu(hMenuPop1, MF_STRING, IDM_TEST, "&测试");  
  7. AppendMenu(hMenuPop2, MF_STRING, IDM_ABOUT, "&关于");  
  8. AppendMenu(hMenuPop2, MF_STRING, IDM_EXIT, "&退出");  
  9. AppendMenu(hMenu, MF_POPUP, (UINT)hMenuPop1, "&菜单1");  
  10. AppendMenu(hMenu, MF_POPUP, (UINT)hMenuPop2, "&菜单2");  

 

(4)创建窗口

[html]  view plain copy
  1. hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,  
  2.         CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, hMenu, hInstance, NULL);  
  3.       
  4.     if (!hWnd)  
  5.     {  
  6.         return FALSE;  
  7.     }  


(5)显示和更新窗口

[html]  view plain copy
  1. ShowWindow(hWnd, nCmdShow);  
  2. UpdateWindow(hWnd);  


(6)创建消息循环

[html]  view plain copy
  1. while (GetMessage(&msg, NULL, 0, 0))   
  2.     {  
  3.         if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))   
  4.         {  
  5.             TranslateMessage(&msg);  
  6.             DispatchMessage(&msg);  
  7.         }  
  8.     }  

获取的消息是虚键消息。TranslateMessage的功能是将虚拟键消息转换为字符消息。DispatchMessage将消息分发给窗口程序。


(7)实现消息响应函数

[html]  view plain copy
  1. #define btn1 1  
  2. #define btn2 2  
  3. LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)  
  4. {  
  5.     int wmId, wmEvent;  
  6.     PAINTSTRUCT ps;  
  7.     HDC hdc;  
  8.     TCHAR szHello[MAX_LOADSTRING];  
  9.     LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);  
  10.       
  11.     switch (message)   
  12.     {  
  13.     case WM_CREATE:  
  14.         {  
  15.             //button  
  16.             HWND hButton1 = CreateWindow(_T("button"), _T("Btn1"),  
  17.                 WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON, 100, 100, 100, 30, hWnd, (HMENU)btn1, hInst, NULL);  
  18.             HWND hButton2 = CreateWindow(_T("button"), _T("Btn2"),  
  19.                 WS_CHILD|WS_VISIBLE|BS_BITMAP, 100, 200, 100, 30, hWnd, (HMENU)btn2, hInst, NULL);  
  20.             HBITMAP hbmp=LoadBitmap(hInst,MAKEINTRESOURCE(IDB_Btn2));  
  21.             SendMessage(hButton2, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM) hbmp);  
  22.         }  
  23.         break;  
  24.     case WM_COMMAND:  
  25.         wmId    = LOWORD(wParam);   
  26.         wmEvent = HIWORD(wParam);   
  27.         // Parse the menu selections:  
  28.         switch (wmId)  
  29.         {  
  30.         case btn1:  
  31.             MessageBox(hWnd, _T("Button1 press!"), _T("Message"), MB_OK|MB_ICONINFORMATION);  
  32.             break;  
  33.         case btn2:  
  34.             MessageBox(hWnd, _T("Button2,press!"), _T("Message"), MB_OK|MB_ICONINFORMATION);  
  35.             break;  
  36.         case IDM_TEST:  
  37.             MessageBox(hWnd, _T("菜单测试"), _T("菜单测试"), MB_OK|MB_ICONINFORMATION);  
  38.             break;  
  39.         case IDM_ABOUT:  
  40.             DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);  
  41.             break;  
  42.         case IDM_EXIT:  
  43.             DestroyWindow(hWnd);  
  44.             break;  
  45.         default:  
  46.             return DefWindowProc(hWnd, message, wParam, lParam);  
  47.         }  
  48.         break;  
  49.         case WM_PAINT:  
  50.             hdc = BeginPaint(hWnd, &ps);  
  51.             // TODO: Add any drawing code here...  
  52.             RECT rt;  
  53.             GetClientRect(hWnd, &rt);  
  54.             DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);  
  55.             EndPaint(hWnd, &ps);  
  56.             break;  
  57.         case WM_DESTROY:  
  58.             PostQuitMessage(0);  
  59.             break;  
  60.         default:  
  61.             return DefWindowProc(hWnd, message, wParam, lParam);  
  62.     }  
  63.     return 0;  
  64. }  
  65.   
  66. // Mesage handler for about box.  
  67. LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)  
  68. {  
  69.     switch (message)  
  70.     {  
  71.     case WM_INITDIALOG:  
  72.         return TRUE;  
  73.           
  74.     case WM_COMMAND:  
  75.         if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)   
  76.         {  
  77.             EndDialog(hDlg, LOWORD(wParam));  
  78.             return TRUE;  
  79.         }  
  80.         break;  
  81.     }  
  82.     return FALSE;  


在WM_CREATE中添加按钮实现代码,在WM_COMMAND中检测按钮响应事件。

 

示例代码的实现效果如下图所示:

 

2 消息响应如何实现

在MFC代码中,没有看到WinMain,也没有看到消息响应函数。但可以发现,利用界面设计器添加的类代码都被包含在一对宏定义中。

[html]  view plain copy
  1. BEGIN_MESSAGE_MAP(CMayMoodDlg, CDialog)  
  2.     //{{AFX_MSG_MAP(CMayMoodDlg)  
  3.     ON_WM_SYSCOMMAND()  
  4.     ON_WM_PAINT()  
  5.     ON_WM_QUERYDRAGICON()  
  6.     ON_BN_CLICKED(IDC_EXIT, OnExit)  
  7.     ON_BN_CLICKED(IDC_BTN_ENCRY, OnBtnEncry)  
  8.     ON_BN_CLICKED(IDC_BTN_DECRY, OnBtnDecry)  
  9.     ON_BN_CLICKED(IDC_BTN_HELP, OnBtnHelp)  
  10.     ON_BN_CLICKED(IDC_BTN_SET, OnBtnSet)  
  11.     ON_BN_CLICKED(IDC_BTN_ABOUT, OnBtnAbout)  
  12.     //}}AFX_MSG_MAP  
  13. END_MESSAGE_MAP()  

BEGIN_MESSAGE_MAP()宏展开如下:

[html]  view plain copy
  1. #ifdef _AFXDLL  
  2. #define BEGIN_MESSAGE_MAP(theClass, baseClass) \  
  3.     const AFX_MSGMAP* PASCAL theClass::_GetBaseMessageMap() \  
  4.         { return &baseClass::messageMap; } \  
  5.     const AFX_MSGMAP* theClass::GetMessageMap() const \  
  6.         { return &theClass::messageMap; } \  
  7.     AFX_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap = \  
  8.     { &theClass::_GetBaseMessageMap, &theClass::_messageEntries[0] }; \  
  9.     AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \  
  10.     { \  
  11.   
  12. #else  
  13. #define BEGIN_MESSAGE_MAP(theClass, baseClass) \  
  14.     const AFX_MSGMAP* theClass::GetMessageMap() const \  
  15.         { return &theClass::messageMap; } \  
  16.     AFX_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap = \  
  17.     { &baseClass::messageMap, &theClass::_messageEntries[0] }; \  
  18.     AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \  
  19.     { \  
  20.   
  21. #endif  

END_MESSAGE_MAP()宏展开如下:

[html]  view plain copy
  1. #define END_MESSAGE_MAP() \  
  2.         {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \  
  3.     }; \  

实际上,这段代码完成了一件事:

在你的类中生成了一个名为_messageEntries的数组,将消息和此消息对应的处理函数填入该数组中。应用程序框架生成的WindowProc接收到一个消息后,会按照一定的原则轮询个各类(CView、CDocument、CFrameWnd、CWinApp)的messageEntries数组,检查该数组中有没有对应的消息,如果有,就调用相应的响应函数;如果没有,就换下一个类继续检查。当所有的有关的类都被检查完后仍未发现响应函数时,便将此消息丢给DefWindowProc处理。 

这其实,说明了一件事:通过上述宏定义完成了对象与消息的连接。这个过程类似于Qt的信号与槽机制。

 关于WinMain的解释网上很多,此处不再赘述。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值