VC++ 自定义消息映射

本文适合与对MFC消息机制有一定了解的学者。在MFC程序中,消息映射是这样定义的:

首先,要用消息处理的类,必须要继承自CcmdTarget类;
然后,在类的声明中有如下的宏:DECLARE_MESSAGE_MAP()和需要实现的消息映射
然后,在类的实现文件中,有如下宏:BEGIN_MESSAGE_MAP(…), … END_MESSAGE_MAP()

请看示例:

头文件中申明:

protected:
	DECLARE_MESSAGE_MAP()
public:
        // 申明一个系统消息
	afx_msg void OnDestroy();
......

在源文件会找到对于的消息映射:

BEGIN_MESSAGE_MAP(CXXXDlg, CDialogEx)
	ON_WM_DESTROY()
......
END_MESSAGE_MAP()

至于在MFC程序中,消息ON_WM_DESTROY是如何与函数OnDestroy()进行关联绑定不是本文介绍的重点。本文是教你如何定义一个自己的消息映射表。

首先请看消息映射DECLARE_COMMAND_MAP()宏定义:

/*
 *	文件名: SWCommandMap.h
 *  说明: 命令执行映射相关的宏定义
 */

#ifndef __SWCOMMANDMAP_H
#define __SWCOMMANDMAP_H

//命令类型定义
typedef ULONG CID_T;

//命令标识常量
#define CID_NULL	0	//没有给定值
#define CID_VOID	0	//给定的为任意

//命令表表项,后面定义
struct COMMAND_MAP_ENTRY;

//命令表项标志
enum COMMAND_MAP_FLAG
{
	COMMAND_MAP_END = 0, //响应表结束标志
    COMMAND_MAP_VV,
};

//命令表项
struct COMMAND_MAP
{
	COMMAND_MAP *pBaseCommandMap;
	COMMAND_MAP_ENTRY *pEntries;
};

//命令表定义宏
#define DECLARE_COMMAND_MAP() \
static COMMAND_MAP_ENTRY _commandEntries[]; \
static COMMAND_MAP commandMap; \
virtual COMMAND_MAP* GetCommandMap() const;

//命令表填充开始宏
#define BEGIN_COMMAND_MAP(theClass, baseClass) \
COMMAND_MAP* theClass::GetCommandMap() const \
{return &theClass::commandMap; } \
COMMAND_MAP theClass::commandMap = \
{(COMMAND_MAP*)&(baseClass::commandMap), \
(COMMAND_MAP_ENTRY*)&(theClass::_commandEntries)}; \
COMMAND_MAP_ENTRY theClass::_commandEntries[] = \
{
//命令表填充结束宏
#define END_COMMAND_MAP() \
{CID_NULL,COMMAND_MAP_END, NULL} \
};

//命令执行函数标记
#ifndef _execute
#define _execute
#endif

//命令执行函数原型
class ISWCommand;
typedef HRESULT (ISWCommand::*PFEXECUTE)(WPARAM wParam, LPARAM lParam);

struct COMMAND_MAP_ENTRY
{
	CID_T		command;	//命令标识
	ULONG		nFlag;		//表项标志
	PFEXECUTE	pfe;		//响应函数
};

//命令执行映射宏
#define MAP_COMMAND(command,pfe) \
	{command,COMMAND_MAP_VV, (PFEXECUTE)pfe},

#endif  //#ifndef __COMMANDMAP_H

有了宏定义,我们就可以定义自己的消息管理类:

// Commander.h: interface for the Commander class.
//
//

#if !defined(AFX_COMMANDER_H__57231F84_60CA_4A96_8A70_2D5EB6CADE6C__INCLUDED_)
#define AFX_COMMANDER_H__57231F84_60CA_4A96_8A70_2D5EB6CADE6C__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "ISWCommandMap.h"

//
//使用命令表的对象的基类
//
class ISWCommand
{
public:
	ISWCommand();
	virtual ~ISWCommand();

public:
	//用一个表来管理命令及执行函数
	DECLARE_COMMAND_MAP()

	//遍历表的操作
	virtual HRESULT DoExecute(CID_T command, WPARAM wParam, LPARAM lParam);
};

#endif // !defined(AFX_COMMANDER_H__57231F84_60CA_4A96_8A70_2D5EB6CADE6C__INCLUDED_)

我们怎么样将消息CID与对应的函数关联起来呢?其实很简单,通过遍历消息表,根据消息CID找到对应的函数,执行相对应的函数体就可以了。

#include "StdAfx.h"
#include "ISWCommand.h"

//static
COMMAND_MAP ISWCommand::commandMap =
{
	NULL,
	&ISWCommand::_commandEntries[0]
};

//static
COMMAND_MAP_ENTRY ISWCommand::_commandEntries[] =
{
	{CID_NULL, COMMAND_MAP_END, NULL},
};

//member
COMMAND_MAP* ISWCommand::GetCommandMap() const
{
	return &ISWCommand::commandMap;
}


//
// Construction/Destruction
//

ISWCommand::ISWCommand()
{
}

ISWCommand::~ISWCommand()
{
}

HRESULT ISWCommand::DoExecute(CID_T command, WPARAM wParam, LPARAM lParam)
{
	//遍历命令表,由最下层子类一直回溯到最上层基类,匹配响应项,
	const COMMAND_MAP *pMap = GetCommandMap();
	BOOL bFind = FALSE;
	HRESULT hr = E_FAIL;
	while(pMap != NULL && !bFind)
	{
		//先分析此层的项
		COMMAND_MAP_ENTRY *pEntries = pMap->pEntries;
		for(int i=0; pEntries[i].nFlag != COMMAND_MAP_END; i++)
		{
			//匹配命令
			if(pEntries[i].command == command)
				//&& pEntries[i].version == version)
			{
				PFEXECUTE pfExecute = pEntries[i].pfe;
				ASSERT(pfExecute != NULL);
				if (pfExecute != NULL)
				{
					hr = (this->*pfExecute)(wParam, lParam);
					bFind = TRUE;
					break;
				}
			}
		}

		//再分析上层的
		pMap = pMap->pBaseCommandMap;
	}

	hr = bFind ? hr : E_NOTIMPL;
	return hr;
}

这样一个简单的自定义消息映射链就完成了。至于怎么使用,我们在自己的类中继承ISWCommand即可。

#pragma once
#include "ISWCommand.h"

#define CID_COMMAND_INIT 1001
#define CID_COMMAND_FREE 1002

class CMyClass: public ISWCommand
{
public:
	CMyClass();
	~CMyClass(void);
public:
    // 自定义消息体
	_execute HRESULT OnInit(WPARAM wParam, LPARAM lParam);
	_execute HRESULT OnFree(WPARAM wParam, LPARAM lParam);
......
	DECLARE_COMMAND_MAP()
protected:
};
BEGIN_COMMAND_MAP(CMyClass, ISWCommand)
	MAP_COMMAND(CID_COMMAND_INIT, &OnInit)
	MAP_COMMAND(CID_COMMAND_FREE, &OnFree)
END_COMMAND_MAP()

HRESULT CMyClass::OnInit(WPARAM wParam, LPARAM lParam)
{
    // TODO
}

HRESULT CMyClass::OnFree(WPARAM wParam, LPARAM lParam)
{
    // TODO
}

 

BEGIN_COMMAND_MAP(CZkManager, ISWCommand)
    ......
	MAP_COMMAND(CID_COMMAND_BASE+1, &OnIst1)//配置当前亮度
	MAP_COMMAND(CID_COMMAND_BASE+2, &OnIst2)//配置当前字体
	MAP_COMMAND(CID_COMMAND_BASE+3, &OnIst3)//配置当前字体颜色
	MAP_COMMAND(CID_COMMAND_BASE+4, &OnIst4)//配置当前画刷颜色
	MAP_COMMAND(CID_COMMAND_BASE+5, &OnIst5)//开机

	MAP_COMMAND(CID_COMMAND_BASE+6, &OnIst6)//关机
	MAP_COMMAND(CID_COMMAND_BASE+7, &OnIst7)//重启
	MAP_COMMAND(CID_COMMAND_BASE+8, &OnIst8)//清屏
	MAP_COMMAND(CID_COMMAND_BASE+9, &OnIst9)//简易点亮点组
	MAP_COMMAND(CID_COMMAND_BASE+10, &OnIst10)//点亮直线

	MAP_COMMAND(CID_COMMAND_BASE+11, &OnIst11)//填充矩形
	MAP_COMMAND(CID_COMMAND_BASE+12, &OnIst12)//立即显示文本
	MAP_COMMAND(CID_COMMAND_BASE+13, &OnIst13)//创建分区
	MAP_COMMAND(CID_COMMAND_BASE+14, &OnIst14)//删除分区
	MAP_COMMAND(CID_COMMAND_BASE+15, &OnIst15)//配置分区属性
    ......
END_COMMAND_MAP()

至此,一个完整的自定义消息类就完成了。

你可能感兴趣的:(VC++(日积月累篇))