如何实现MFC消息映射表

原文链接

上一篇文章学习了Windows应用程序的简单实现,如果你还不了解其流程,请点击下面的链接:

Windows应用程序简介
接下来我们来研究一下MFC的消息映射表是如何实现的。

在MFC中,我们只需要定义我们的消息处理函数,以及在BEGIN_MESSAGE_MAP和END_MESSAGE_MAP中添加对应关系,它是如何实现的呢?下面我们就来看一下吧。

Windows应用程序简介

  1. 定义消息结构体,声明映射函数
//构造消息映射表格
struct MSGMAP_ENTRY {
    UINT nMessage;  //消息类型
    LONG (*pfn)(HWND, UINT, WPARAM, LPARAM);    //消息处理函数
};
#define dim(x) (sizeof(x) / sizeof(x[0]))

LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam );

//消息映射函数声明
LONG OnCommand(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
LONG OnClose(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
LONG OnDestroy(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParamv);
LONG OnLButtonDown(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
LONG OnPaint(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

在该结构体中,消息类型和消息处理函数一一对应。

  1. 初始化消息和映射函数对照表
//消息与处理函数对照表
struct MSGMAP_ENTRY _messageEntries[] =
{
    WM_PAINT, OnPaint,
    WM_COMMAND, OnCommand,
    WM_CLOSE, OnClose,
    WM_DESTROY, OnDestroy,
    WM_LBUTTONDOWN, OnLButtonDown,
} ;
//Command消息与处理函数对照表
struct MSGMAP_ENTRY _commandEntries[] =
{
    0,0,    //数组长度不能为0,补空值
    //IDM_FILEOPEN, OnFileOpen,
    //IDM_SAVEAS, OnSaveAs,
} ;
  1. 重新实现窗口过程
//窗口过程
LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
    for(int i = 0; i < dim(_messageEntries); i++) {
        // 如果不在数组中,则丢给默认处理函数
        if(message == _messageEntries[i].nMessage) {
            return _messageEntries[i].pfn(hWnd, message, wParam, lParam);
        }
    }
    return DefWindowProc(hWnd, message, wParam, lParam);
}
  1. 实现消息映射函数
//同窗口过程,只处理WM_Command消息
LONG OnCommand( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
    for(int i = 0; i < dim(_commandEntries); i++) {
        if(LOWORD(wParam) == _commandEntries[i].nMessage) {
            return _commandEntries[i].pfn(hWnd, message, wParam, lParam);
        }
    }
    return DefWindowProc(hWnd, message, wParam, lParam);
}

LONG OnClose( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
    DestroyWindow(hWnd);    //仅关闭窗口,没有关闭进程,并发送另一个消息WM_DESTROY
    return TRUE;
}

LONG OnDestroy( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParamv )
{
    PostQuitMessage(0);     //真正的关闭进程
    return TRUE;
}
//省略部分消息映射函数实现

这样设计之后,我们的WndProc和OnCommand函数便一般化了,每当有新的消息需要处理,我们只需要在_messageEntries和_commandEntries中添加消息类型和映射函数即可,且不需要修改WndProc和OnCommand函数。

结合上一篇的注册,创建窗口,就可运行起来了,大家可以一起尝试一下。

以上实现就是MFC的MessageMap雏形,不过MFC包装的更好更复杂。下面我们可以简单看一下MFC是如何实现的。

  1. 在MFC中实现消息映射
//.h文件
afx_msg void OnPaint();

//.cpp文件
BEGIN_MESSAGE_MAP(ThisClass, BaseClass)
ON_WM_PAINT()
END_MESSAGE_MAP()
  1. 跟踪BEGIN_MESSAGE_MAP和END_MESSAGE_MAP宏
PTM_WARNING_DISABLE     // 告警抑制

// 定义获取消息映射表的函数
const AFX_MSGMAP* theClass::GetMessageMap() const 
{
    return GetThisMessageMap(); 
} 
const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap() 
{ 
    typedef theClass ThisClass;
    typedef baseClass TheBaseClass;

    // 添加消息映射的数组
    static const AFX_MSGMAP_ENTRY _messageEntries[] =  
    {
        ON_WM_PAINT();  // 我们添加的内容,ON_WM_PAINT宏将后续参数转换为AFX_MSGMAP_ENTRY格式,常用消息已经统一,不需要参数
        {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 }     // 数组初始化长度不能为0,为防止我们没有添加内容而导致出错而添加0值
    }; 
    // 构建子类的消息映射表结构
    static const AFX_MSGMAP messageMap = 
    { &TheBaseClass::GetThisMessageMap, &_messageEntries[0] }; 
    return &messageMap;    //返回此类的消息映射表
}                                  
PTM_WARNING_RESTORE

这里我删掉了续行符,并添加了一下换行和缩减,方便大家阅读和理解。

  1. 接下来我们看一下AFX_MSGMAP_ENTRY结构体及ON_WM_PAINT宏:
struct AFX_MSGMAP_ENTRY
{
    UINT nMessage;   // windows message
    UINT nCode;      // control code or WM_NOTIFY code
    UINT nID;        // control ID (or 0 for windows messages)
    UINT nLastID;    // used for entries specifying a range of control id's
    UINT_PTR nSig;       // signature type (action) or pointer to message #
    AFX_PMSG pfn;    // routine to call (or special value)
};

#define ON_WM_PAINT()
    { WM_PAINT, 0, 0, 0, AfxSig_vv, \
        (AFX_PMSG)(AFX_PMSGW) \
        (static_cast< void (AFX_MSG_CALL CWnd::*)(void) > ( &ThisClass :: OnPaint)) },

可以看到,对于系统提供的消息,我们只用添加固定的宏即可,如果是自己添加的控件,则需要添加资源ID和映射函数。

参考文献:
[1]《深入浅出MFC》第二版 华中科技大学出版社 侯俊杰

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值