MFC中的消息映射

MFC中消息处理是封装好的,他会根据消息是调用对应的对象的处理函数,而MFC是怎么做到的呢?

MFC是会自己建一张消息映射表,而自己分发消息就得先获取消息了,这就用到了HOOK了,这样获取到消息后,自己就可以对照MFC中的消息映射表去执行对应的对象的处理函数

MFC消息映射机制执行步骤是:当消息发生,我们用HOOK技术把本来要发送到窗口过程的消息抓获,然后对照一下MFC窗口的消息映射表,如果是表里面有的消息,就执行其对应的函数

 

我们分析一下MFC中的消息映射表是怎么做到的:主要是是BEGIN_MESSAGE_MAP和END_MESSAGE_MAP两个宏,其实宏的作用也就是给消息映射表结构体 struct AFX_MSGMAP赋值,一个是自己的消息映射表,一个是基类的消息映射表地址,这样的话就形成了一个单链表,自己的没有的就可以去链表中一层一层查找,其实这种机制用虚函数也可以实现,但是为什么不用虚函数呢,因为MFC中的继承封装的比较深,而且消息处理函数也多,如果基类都来一个虚函数处理的空响应,而我子类只需要响应一个消息处理,那每创建一个基类的对象就会有多余的虚表,虚表就很多多余的函数,这样的话开销太大,而用消息映射表的话就不会有这个问题,因为他是静态的,所有这个类型的对象都是共享着一张表,只会生成一份

消息映射表结构体 struct AFX_MSGMAP

//消息映射表结构
struct AFX_MSGMAP_ENTRY
{
	UINT nMessage;   // windows message	Windows消息
	UINT nCode;      // control code or WM_NOTIFY code 控制代码或WM_NOTIFY代码
	UINT nID;        // control ID (or 0 for windows messages)  控件ID(或Windows消息为0)
	UINT nLastID;    // used for entries specifying a range of control id's  用于指定控件ID范围的条目
	UINT_PTR nSig;   // signature type (action) or pointer to message #签名类型(操作)或指向消息的指针
	AFX_PMSG pfn;    // routine to call (or special value)  函数指针
};


struct AFX_MSGMAP
{
	const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();//获取基类的消息映射表
	const AFX_MSGMAP_ENTRY* lpEntries;//本类的消息映射表
};

下面看代码宏展开例子:我有两个类CMFCRemoteControlClientDlg和基类CDialogEx  

#define DECLARE_MESSAGE_MAP() //消息映射声明
protected: 
	static const AFX_MSGMAP* PASCAL GetThisMessageMap();//获取自己类的消息映射 
	virtual const AFX_MSGMAP* GetMessageMap() const; //封装了一个函数,调用就是GetThisMessageMap


//实现需要三个宏进行初始化和定义函数,主要功能是把消息和消息处理函数形成一个对应的表
下面例子就是把IDC_BUTTON1消息对应&CMFCRemoteControlClientDlg::OnBnClickedButton1处理函数,这个表是AFX_MSGMAP类型结构体,保存本类的消息映射表和指向父类的消息映射
//实现的3个宏
BEGIN_MESSAGE_MAP(CMFCRemoteControlClientDlg, CDialogEx)
    ON_BN_CLICKED(IDC_BUTTON1, &CMFCRemoteControlClientDlg::OnBnClickedButton1)
END_MESSAGE_MAP()
//宏展开如下,功能主要就是给AFX_MSGMAP messageMap结构体赋值形成消息映射表
#define BEGIN_MESSAGE_MAP(CMFCRemoteControlClientDlg, CDialogEx) 
const AFX_MSGMAP* CMFCRemoteControlClientDlg::GetMessageMap() const 
{ 
	return GetThisMessageMap(); 
}		
const AFX_MSGMAP* PASCAL CMFCRemoteControlClientDlg::GetThisMessageMap() 
{ 
	typedef CMFCRemoteControlClientDlg ThisClass;//本类取个别名
	typedef CDialogEx TheBaseClass;		//基类取个别名	
	static const AFX_MSGMAP_ENTRY _messageEntries[] =  
	{
		//定义_messageEntries
		ON_BN_CLICKED(IDC_BUTTON1, &CMFCRemoteControlClientDlg::OnBnClickedButton1)
		{ WM_COMMAND, (WORD)wNotifyCode, (WORD)IDC_BUTTON1, (WORD)IDC_BUTTON1, AfxSigCmd_v, (static_cast< AFX_PMSG > (OnBnClickedButton1)) },
		#define END_MESSAGE_MAP() 
		{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } 
	}; 
	//初始化消息映射表,并保存父类的
	static const AFX_MSGMAP messageMap = 
	{ 
		&TheBaseClass::GetThisMessageMap, &_messageEntries[0] 
	}; 
	return &messageMap; //返回消息映射图表
}	

模拟MFC消息映射,假设我的窗口过程函数已经使用HOOK替换了原来的窗口过程函数,这时我只需要在自己的窗口过程函数中

查找自己的表去处理就可以了

CWnd.h

//处理函数声明的结构类型
enum AfxSig
{
  AfxSig_end,
  AfxSig_v_v, //void(void)
  AfxSig_v_iii,//void(int, int, int)
  AfxSig_v_ii,//void(int, int)
  AfxSig_i_p //void(LPCREATESTRUCT)
};

//消息映射表结构体
class CWnd;
typedef void (CWnd::*PWNDMSG)(void);
struct AFXMSG_ENTRY 
{
  UINT nMessage;//消息Id
  PWNDMSG pfn;//消息处理函数指针
  UINT nSig;//函数声明类型
};

//函数指针联合体
union MessageFunctions
{
  void(CWnd::*pfn_v_v)();
  void(CWnd::*pfn_v_iii)(int, int, int);
  void(CWnd::*pfn_v_ii)(int, int);
  int(CWnd::*pfn_v_p)(LPCREATESTRUCT);
};

//消息映射表链表结构节点
struct AFX_MSGMAP
{
  AFXMSG_ENTRY* pEntries;
  AFX_MSGMAP* pBaseMsgMap; 
};

class CWnd : public CCmdTarget  
{
	virtual AFX_MSGMAP* GetMessagemap(); //获取消息映射表
	static AFX_MSGMAP messageMap;//消息映射表结构体
	static AFXMSG_ENTRY _messages[];//实际消息映射表结构体
	
	//消息处理函数
	void OnClose();
	void OnDestroy();
	void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
	void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags);
	void OnLButtonDown(UINT nX, UINT nY);
	void OnLButtonUp(UINT nX, UINT nY);
	int OnCreate(LPCREATESTRUCT lpCreateStruct);
	
	//窗口处理函数
	LRESULT WindowProc(  HWND hwnd,      // handle to window
    UINT uMsg,      // message identifier
    WPARAM wParam,  // first message parameter
    LPARAM lParam   // second message parameter
    );
};

CWnd.cpp

AFX_MSGMAP* CWnd::GetMessagemap()
{
  return &s_messageMap;
} 

AFX_MSGMAP CWnd::s_messageMap = {s_messages, NULL};

AFXMSG_ENTRY CWnd::s_messages[] = 
{
  {WM_CLOSE, CWnd::OnClose, AfxSig_v_v},
  {WM_DESTROY, CWnd::OnDestroy, AfxSig_v_v},
  {WM_KEYDOWN, (PWNDMSG)CWnd::OnKeyDown, AfxSig_v_iii},
  {WM_KEYUP, (PWNDMSG)CWnd::OnKeyUp, AfxSig_v_iii},
  {WM_LBUTTONDOWN, (PWNDMSG)CWnd::OnLButtonDown, AfxSig_v_ii},
  {WM_LBUTTONUP, (PWNDMSG)CWnd::OnLButtonUp, AfxSig_v_ii},
  {WM_CREATE, (PWNDMSG)CWnd::OnCreate, AfxSig_i_p},
  {0, (PWNDMSG)0, AfxSig_end}
};


LRESULT CWnd::WindowProc(  HWND hwnd, 
  UINT uMsg,      // message identifier
  WPARAM wParam,  // first message parameter
  LPARAM lParam   // second message parameter
  )
{
  
  AFX_MSGMAP* pMsgmap = GetMessagemap();
  
  //从子类的消息映射表开始,向上逐表查询
  while(pMsgmap != NULL)
  {
    // 查表
    AFXMSG_ENTRY* pEntries = pMsgmap->pEntries;

    for(int i = 0; pEntries[i].nSig != AfxSig_end; ++i)
    {
      if(pEntries[i].nMessage == uMsg)
      {
	    //函数指针的联合体,根据不同函数声明,调用不同的函数指针
        MessageFunctions mmf;
        mmf.pfn_v_v = pEntries[i].pfn;
        LRESULT lResult = 0;
        
        switch(pEntries[i].nSig)
        {
        case AfxSig_v_v:
          {
            (this->*pEntries[i].pfn)();
            break;
          }
        case AfxSig_v_iii:
          {
            (this->*mmf.pfn_v_iii)(wParam, LOWORD(lParam), HIWORD(lParam));
            break;
          }
        case AfxSig_v_ii:
          {
            (this->*mmf.pfn_v_ii)(LOWORD(lParam), HIWORD(lParam));
            break;
          }
        case AfxSig_i_p:
          {
            lResult = (this->*mmf.pfn_v_p)((LPCREATESTRUCT)lParam);
            break;
          }
        }
        
        return lResult;
      }
    }

    pMsgmap = pMsgmap->pBaseMsgMap;
  }

  //这个是用HOOK替换前的窗口过程,如果没有自己定义消息处理函数,调用原来的消息处理函数
  return m_pOldProc(hwnd, uMsg, wParam, lParam);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值