也说MFC消息响应机制

来源:互联网 发布:windows 显卡切换 编辑:程序博客网 时间:2024/04/25 03:24

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

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

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

【解析】

1 利用代码编写对话框

先看代码:

main.cpp

#include "stdafx.h"#include "resource.h"#include "tchar.h"#define MAX_LOADSTRING 100// Global Variables:HINSTANCE hInst;// current instanceTCHAR szTitle[MAX_LOADSTRING];// The title bar textTCHAR szWindowClass[MAX_LOADSTRING];// The title bar text// Foward declarations of functions included in this code module:ATOMMyRegisterClass(HINSTANCE hInstance);BOOLInitInstance(HINSTANCE, int);LRESULT CALLBACKWndProc(HWND, UINT, WPARAM, LPARAM);LRESULT CALLBACKAbout(HWND, UINT, WPARAM, LPARAM);int APIENTRY WinMain(HINSTANCE hInstance,                     HINSTANCE hPrevInstance,                     LPSTR     lpCmdLine,                     int       nCmdShow){// TODO: Place code here.MSG msg;HACCEL hAccelTable;// Initialize global stringsLoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);LoadString(hInstance, IDC_BTNTEST, szWindowClass, MAX_LOADSTRING);MyRegisterClass(hInstance);// Perform application initialization:if (!InitInstance (hInstance, nCmdShow)) {return FALSE;}hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_BTNTEST);// Main message loop:while (GetMessage(&msg, NULL, 0, 0)) {if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {TranslateMessage(&msg);DispatchMessage(&msg);}}return msg.wParam;}////  FUNCTION: MyRegisterClass()////  PURPOSE: Registers the window class.////  COMMENTS:////    This function and its usage is only necessary if you want this code//    to be compatible with Win32 systems prior to the 'RegisterClassEx'//    function that was added to Windows 95. It is important to call this function//    so that the application will get 'well formed' small icons associated//    with it.//ATOM MyRegisterClass(HINSTANCE hInstance){WNDCLASSEX wcex;wcex.cbSize = sizeof(WNDCLASSEX); wcex.style= CS_HREDRAW | CS_VREDRAW;wcex.lpfnWndProc= (WNDPROC)WndProc;wcex.cbClsExtra= 0;wcex.cbWndExtra= 0;wcex.hInstance= hInstance;wcex.hIcon= LoadIcon(hInstance, (LPCTSTR)IDI_BTNTEST);wcex.hCursor= LoadCursor(NULL, IDC_ARROW);wcex.hbrBackground= (HBRUSH)(COLOR_WINDOW+1);wcex.lpszMenuName= (LPCSTR)IDC_BTNTEST;wcex.lpszClassName= szWindowClass;wcex.hIconSm= LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);return RegisterClassEx(&wcex);}////   FUNCTION: InitInstance(HANDLE, int)////   PURPOSE: Saves instance handle and creates main window////   COMMENTS:////        In this function, we save the instance handle in a global variable and//        create and display the main program window.//BOOL InitInstance(HINSTANCE hInstance, int nCmdShow){HWND hWnd;hInst = hInstance; // Store instance handle in our global variable//menustatic HMENU hMenu,hMenuPop1,hMenuPop2;hMenu = CreateMenu();hMenuPop1 = CreateMenu();hMenuPop2 = CreateMenu();AppendMenu(hMenuPop1, MF_STRING, IDM_TEST, "&测试");AppendMenu(hMenuPop2, MF_STRING, IDM_ABOUT, "&关于");AppendMenu(hMenuPop2, MF_STRING, IDM_EXIT, "&退出");AppendMenu(hMenu, MF_POPUP, (UINT)hMenuPop1, "&菜单1");AppendMenu(hMenu, MF_POPUP, (UINT)hMenuPop2, "&菜单2");hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, hMenu, hInstance, NULL);if (!hWnd){return FALSE;}ShowWindow(hWnd, nCmdShow);UpdateWindow(hWnd);return TRUE;}////  FUNCTION: WndProc(HWND, unsigned, WORD, LONG)////  PURPOSE:  Processes messages for the main window.////  WM_COMMAND- process the application menu//  WM_PAINT- Paint the main window//  WM_DESTROY- post a quit message and return////#define btn1 1#define btn2 2LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){int wmId, wmEvent;PAINTSTRUCT ps;HDC hdc;TCHAR szHello[MAX_LOADSTRING];LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);switch (message) {case WM_CREATE:{//buttonHWND hButton1 = CreateWindow(_T("button"), _T("Btn1"),WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON, 100, 100, 100, 30, hWnd, (HMENU)btn1, hInst, NULL);HWND hButton2 = CreateWindow(_T("button"), _T("Btn2"),WS_CHILD|WS_VISIBLE|BS_BITMAP, 100, 200, 100, 30, hWnd, (HMENU)btn2, hInst, NULL);HBITMAP hbmp=LoadBitmap(hInst,MAKEINTRESOURCE(IDB_Btn2));SendMessage(hButton2, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM) hbmp);}break;case WM_COMMAND:wmId    = LOWORD(wParam); wmEvent = HIWORD(wParam); // Parse the menu selections:switch (wmId){case btn1:MessageBox(hWnd, _T("Button1 press!"), _T("Message"), MB_OK|MB_ICONINFORMATION);break;case btn2:MessageBox(hWnd, _T("Button2,press!"), _T("Message"), MB_OK|MB_ICONINFORMATION);break;case IDM_TEST:MessageBox(hWnd, _T("菜单测试"), _T("菜单测试"), MB_OK|MB_ICONINFORMATION);break;case IDM_ABOUT:DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);break;case IDM_EXIT:DestroyWindow(hWnd);break;default:return DefWindowProc(hWnd, message, wParam, lParam);}break;case WM_PAINT:hdc = BeginPaint(hWnd, &ps);// TODO: Add any drawing code here...RECT rt;GetClientRect(hWnd, &rt);DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);EndPaint(hWnd, &ps);break;case WM_DESTROY:PostQuitMessage(0);break;default:return DefWindowProc(hWnd, message, wParam, lParam);}return 0;}// Mesage handler for about box.LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam){switch (message){case WM_INITDIALOG:return TRUE;case WM_COMMAND:if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {EndDialog(hDlg, LOWORD(wParam));return TRUE;}break;}    return FALSE;}


resource.h

//{{NO_DEPENDENCIES}}// Microsoft Developer Studio generated include file.// Used by Test2.rc//#define IDS_APP_TITLE                   1#define IDC_BTNTEST                     2#define IDS_HELLO                       3#define IDI_BTNTEST                     101#define IDI_SMALL                       102#define IDD_ABOUTBOX                    103#define IDR_MENU1                       110#define IDB_Btn2                        201#define IDM_ABOUT                       1001#define IDM_EXIT                        1002#define IDM_TEST1003// Next default values for new objects// #ifdef APSTUDIO_INVOKED#ifndef APSTUDIO_READONLY_SYMBOLS#define _APS_NEXT_RESOURCE_VALUE        113#define _APS_NEXT_COMMAND_VALUE         40001#define _APS_NEXT_CONTROL_VALUE         1000#define _APS_NEXT_SYMED_VALUE           101#endif#endif

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

WNDCLASSEX wcex;wcex.cbSize = sizeof(WNDCLASSEX); wcex.style= CS_HREDRAW | CS_VREDRAW;wcex.lpfnWndProc= (WNDPROC)WndProc;wcex.cbClsExtra= 0;wcex.cbWndExtra= 0;wcex.hInstance= hInstance;wcex.hIcon= LoadIcon(hInstance, (LPCTSTR)IDI_BTNTEST);wcex.hCursor= LoadCursor(NULL, IDC_ARROW);wcex.hbrBackground= (HBRUSH)(COLOR_WINDOW+1);wcex.lpszMenuName= (LPCSTR)IDC_BTNTEST;wcex.lpszClassName= szWindowClass;wcex.hIconSm= LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);

 最为重要的一行代码:

wcex.lpfnWndProc= (WNDPROC)WndProc;

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

(2)注册窗口类

return RegisterClassEx(&wcex);


(3) 添加菜单

static HMENU hMenu,hMenuPop1,hMenuPop2;hMenu = CreateMenu();hMenuPop1 = CreateMenu();hMenuPop2 = CreateMenu();AppendMenu(hMenuPop1, MF_STRING, IDM_TEST, "&测试");AppendMenu(hMenuPop2, MF_STRING, IDM_ABOUT, "&关于");AppendMenu(hMenuPop2, MF_STRING, IDM_EXIT, "&退出");AppendMenu(hMenu, MF_POPUP, (UINT)hMenuPop1, "&菜单1");AppendMenu(hMenu, MF_POPUP, (UINT)hMenuPop2, "&菜单2");

 

(4)创建窗口

hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, hMenu, hInstance, NULL);if (!hWnd){return FALSE;}


(5)显示和更新窗口

ShowWindow(hWnd, nCmdShow);UpdateWindow(hWnd);


(6)创建消息循环

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

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


(7)实现消息响应函数

#define btn1 1#define btn2 2LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){int wmId, wmEvent;PAINTSTRUCT ps;HDC hdc;TCHAR szHello[MAX_LOADSTRING];LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);switch (message) {case WM_CREATE:{//buttonHWND hButton1 = CreateWindow(_T("button"), _T("Btn1"),WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON, 100, 100, 100, 30, hWnd, (HMENU)btn1, hInst, NULL);HWND hButton2 = CreateWindow(_T("button"), _T("Btn2"),WS_CHILD|WS_VISIBLE|BS_BITMAP, 100, 200, 100, 30, hWnd, (HMENU)btn2, hInst, NULL);HBITMAP hbmp=LoadBitmap(hInst,MAKEINTRESOURCE(IDB_Btn2));SendMessage(hButton2, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM) hbmp);}break;case WM_COMMAND:wmId    = LOWORD(wParam); wmEvent = HIWORD(wParam); // Parse the menu selections:switch (wmId){case btn1:MessageBox(hWnd, _T("Button1 press!"), _T("Message"), MB_OK|MB_ICONINFORMATION);break;case btn2:MessageBox(hWnd, _T("Button2,press!"), _T("Message"), MB_OK|MB_ICONINFORMATION);break;case IDM_TEST:MessageBox(hWnd, _T("菜单测试"), _T("菜单测试"), MB_OK|MB_ICONINFORMATION);break;case IDM_ABOUT:DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);break;case IDM_EXIT:DestroyWindow(hWnd);break;default:return DefWindowProc(hWnd, message, wParam, lParam);}break;case WM_PAINT:hdc = BeginPaint(hWnd, &ps);// TODO: Add any drawing code here...RECT rt;GetClientRect(hWnd, &rt);DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);EndPaint(hWnd, &ps);break;case WM_DESTROY:PostQuitMessage(0);break;default:return DefWindowProc(hWnd, message, wParam, lParam);}return 0;}// Mesage handler for about box.LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam){switch (message){case WM_INITDIALOG:return TRUE;case WM_COMMAND:if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {EndDialog(hDlg, LOWORD(wParam));return TRUE;}break;}    return FALSE;


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

 

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

 

2 消息响应如何实现

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

BEGIN_MESSAGE_MAP(CMayMoodDlg, CDialog)//{{AFX_MSG_MAP(CMayMoodDlg)ON_WM_SYSCOMMAND()ON_WM_PAINT()ON_WM_QUERYDRAGICON()ON_BN_CLICKED(IDC_EXIT, OnExit)ON_BN_CLICKED(IDC_BTN_ENCRY, OnBtnEncry)ON_BN_CLICKED(IDC_BTN_DECRY, OnBtnDecry)ON_BN_CLICKED(IDC_BTN_HELP, OnBtnHelp)ON_BN_CLICKED(IDC_BTN_SET, OnBtnSet)ON_BN_CLICKED(IDC_BTN_ABOUT, OnBtnAbout)//}}AFX_MSG_MAPEND_MESSAGE_MAP()

BEGIN_MESSAGE_MAP()宏展开如下:

#ifdef _AFXDLL#define BEGIN_MESSAGE_MAP(theClass, baseClass) \const AFX_MSGMAP* PASCAL theClass::_GetBaseMessageMap() \{ return &baseClass::messageMap; } \const AFX_MSGMAP* theClass::GetMessageMap() const \{ return &theClass::messageMap; } \AFX_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap = \{ &theClass::_GetBaseMessageMap, &theClass::_messageEntries[0] }; \AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \{ \#else#define BEGIN_MESSAGE_MAP(theClass, baseClass) \const AFX_MSGMAP* theClass::GetMessageMap() const \{ return &theClass::messageMap; } \AFX_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap = \{ &baseClass::messageMap, &theClass::_messageEntries[0] }; \AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \{ \#endif

END_MESSAGE_MAP()宏展开如下:

#define END_MESSAGE_MAP() \{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \}; \

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

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

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

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

 

【源码下载】

1 http://download.csdn.net/detail/tandesir/4949921

 

【学习参考】

1 http://blog.csdn.net/zeng622peng/article/details/5589867

2 http://blog.csdn.net/luckyboy101/article/details/6739701

 

 

转载请标明出处,仅供学习交流,勿用于商业目的

Copyright @ http://blog.csdn.net/tandesir

 

原创粉丝点击