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);
}