在前面一文中,可以看到,处理消息的窗口函数是一个switch-case结构,当然这不是处理多分支程序的唯一结构。下面将会给出一种更模块化的结构。
首先,为了使Windows SDK程序结构更清晰,利用C函数把程序模块化,对Windows程序进行封装。
程序代码可按如下设计:
#include<windows.h> //定义全局变量-------------------------------------------------------------------------------- HINSTANCE hInst; HWND hWnd; MSG msg; char lpszClassName[]="窗口"; char*ShowText; //声明函数原型-------------------------------------------------------------------------------------- ATOM MyRegisterClass(HINSTANCE hInstance);//注册窗口类函数 BOOL Create(HINSTANCE, int); //程序实例初始化函数 int Run(); //消息循环函数 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //窗口函数 void OnLButtonDown(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); void OnPaint(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); void OnDestroy(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); //主函数----------------------------------------------------------------------------------------- int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MyRegisterClass(hInstance); //定义和注册窗口类 Create(hInstance, nCmdShow); //创建窗口 ShowWindow(hWnd, nCmdShow); //显示窗口 UpdateWindow(hWnd); //更新屏幕显示 return Run(); //消息循环 } //注册窗口类函数的实现-------------------------------------------------------------------------- ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASS wc; wc.style=0; wc.lpfnWndProc=WndProc; wc.cbClsExtra=0; wc.cbWndExtra=0; wc.hInstance=hInstance; wc.hIcon=LoadIcon(NULL,IDI_APPLICATION); wc.hCursor=LoadCursor(NULL,IDC_ARROW); wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName=NULL; wc.lpszClassName=lpszClassName; return RegisterClass(&wc); } //创建窗口函数的实现----------------------------------------------------------------------------- BOOL Create(HINSTANCE hInstance, int nCmdShow) { hWnd=CreateWindow( lpszClassName, "Windows", WS_OVERLAPPEDWINDOW, 400,300,180,160, NULL, NULL, hInstance, NULL); return TRUE; } //消息循环函数的实现----------------------------------------------------------------------------- int Run( ) { while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } //窗口函数的实现-------------------------------------------------------------------------------------- LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_LBUTTONDOWN: OnLButtonDown(hWnd, message, wParam, lParam); break; case WM_PAINT: OnPaint(hWnd, message,wParam, lParam); break; case WM_DESTROY: OnDestroy(hWnd, message, wParam, lParam); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } void OnLButtonDown(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { ShowText="Hello!"; InvalidateRect(hWnd,NULL,1); } void OnPaint(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; HDC hdc; hdc = BeginPaint(hWnd, &ps); TextOut(hdc,50,50,ShowText,6); EndPaint(hWnd, &ps); } void OnDestroy(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { PostQuitMessage(0); }
从上面代码可以看出,程序由两大部分组成:主函数部分和窗口函数部分。主函数由五个函数组成,窗口函数由四个函数组成。这样使得程序的结构更见清晰。
上面随实现了部分功能,但并没有从根本上处理消息处理,下面展示一种接近MFC的处理过程。
1.先把各个消息处理程序封装成函数,制作一个如图1-1的消息与消息处理函数之间的对应关系,这个表可以叫做消息映射表。
消息标示 | 消息处理函数 |
WM_LBUTTONDOWN | On_LButtonDown |
WM_PAINT | On_Paint |
WM_RESTROY | On_Restroy |
图1-1 消息映射表
在程序中,可以用数组或者链表来实现这个表,这里简单用数组实现,然后设计数组结构如下:
struct MSGMAP_ENTRY { UINT nMessage; //消息标示 void (*pfn)(HWND, UINT, WPARAM, LPARAM); //消息处理函数指针 };
于是,作为消息映射表的数组应该为:
struct MSGMAP_ENTRY messageEntres[] = { WM_LBUTTONDOWN, On_LButtonDown, WM_PAINT, On_Paint, WM_DESTROY, On_Destroy, };
2.这样窗口函数就可以这样设计:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int i; int n = sizeof (messageEntres) / sizeof(messageEntres[0]); for (i = 0; i < n; i ++) { if (message == messageEntres[i].nMessage) (*messageEntres[i].pfn)(hWnd, message, wParam, lParam); } return DefWindowProc(hWnd, message, wParam, lParam); }
这样,无论需要处理多少消息,窗口函数的结构都被固定成这种格式。于是就可以再编程是自动产生这个函数,而用户所需要做的工作就是编写消息处理函数,并按上面消息结构将消息和消息处理函数填入MSGMAP_ENTRY中。
为了使程序结构更接近MFC,我们一步步进行处理,将这些消息处理定义为宏。这样代码将如下:
//头文件 #ifndef MYWINDOWS #define MYWINDOWS #include <Windows.h> struct MSGMAP_ENTRY { UINT nMessage; void (*pfn)(HWND, UINT, WPARAM, LPARAM); }; ATOM MyRegisterClass(HINSTANCE hInstance); HWND Create(HINSTANCE, int); int Run(); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); void On_LButtonDown(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); void On_Paint(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); void On_Destroy(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); #define DECLARE_MESSAGE_MAP() \ struct MSGMAP_ENTRY messageEntres[]; \ #define BEGIN_MESSAGE_MAP() \ struct MSGMAP_ENTRY messageEntres[] = \ { \ #define ON_WM(messageID, msgFuc) \ messageID, msgFuc, #define END_MESSAGE_MAP() \ }; \ #endif
//实现文件 #include "feizhuang.h" MSG msg; char lpszClassName[] = "窗口"; char *ShowText; HINSTANCE hInstance; HWND hWnd; //声明消息映射表 DECLARE_MESSAGE_MAP()
//实现消息映射表 BEGIN_MESSAGE_MAP() ON_WM(WM_LBUTTONDOWN, On_LButtonDown) ON_WM(WM_PAINT, On_Paint) ON_WM(WM_DESTROY, On_Destroy) END_MESSAGE_MAP() ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASS wc; wc.style = 0; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL,IDI_APPLICATION); wc.hCursor = LoadCursor(NULL,IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = lpszClassName; return RegisterClass(&wc); } HWND Create(HINSTANCE hInstance, int nCmdShow) { hWnd = CreateWindow(lpszClassName, "Windows", WS_OVERLAPPEDWINDOW, 400,300,500,500, NULL, NULL, hInstance, NULL); return hWnd; } int Run() { while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
int i;
int n = sizeof (messageEntres) / sizeof(messageEntres[0]);
for (i = 0; i < n; i ++)
{
if (message == messageEntres[i].nMessage)
(*messageEntres[i].pfn)(hWnd, message, wParam, lParam);
}
return DefWindowProc(hWnd, message, wParam, lParam);
} void On_LButtonDown(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { ShowText = "Hello!"; InvalidateRect(hWnd,NULL,1); } void On_Paint(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; HDC hdc; hdc = BeginPaint(hWnd, &ps); TextOut(hdc, 50, 50, ShowText, 6); EndPaint(hWnd, &ps); } void On_Destroy(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { PostQuitMessage(0); }
下一篇,可以使用C++特性将这些处理封装成类。