MFC消息映射浅谈(二)

前面一文中,可以看到,处理消息的窗口函数是一个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++特性将这些处理封装成类。

 

 

 

posted @ 2012-10-22 21:55  Loylin  阅读(1201)  评论(0编辑  收藏  举报