一、消息映射机制
1.消息映射机制的使用
(1)类必须派生自CCmdTarget
(2)类内必须添加声明宏:DECLARE_MESSAGE_MAP( )
(3)类外必须添加实现宏:BEGIN_MESSAGE_MAP( theClass, baseClass )
END_MESSAGE_MAP( )
2.消息映射机制的实现
(1)数据结构
struct AFX_MSGMAP_ENTRY
{
UINT nMessage; // 消息ID
UINT nCode; // 控件通知码(只有控件的WM_COMMAND消息有通知码)
UINT nID; // 命令ID、控件ID
UINT nLastID; // 最后一个控件ID
UINT_PTR nSig; // 消息处理函数类型
AFX_PMSG pfn; // 消息处理函数的指针(地址)
};
struct AFX_MSGMAP
{
const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)( );
const AFX_MSGMAP_ENTRY* lpEntries;
};
(2)宏展开的代码
见代码
(3)宏展开各部分的作用
_messageEntries[ ] - 静态数组
数组中每个元素保存了消息ID和处理消息的函数地址等
messageMap - 静态变量
保存父类静态变量地址(负责连接链表)
保存了本类静态数组首地址
GetMessageMap( )
获取本类静态变量地址(获取链表头结点)
(4)关系
GetMessageMap( )
|->CMyFrameWnd::messageMap
|->&CMyFrameWnd::_messageEntries[ ]
|->消息ID ... 处理函数
|->&CFrameWnd::messageMap
|->&CFrameWnd::_messageEntries[ ]
|->消息ID ... 处理函数
|->&CWnd::messageMap
|->&CWnd::_messageEntries[ ]
|->消息ID ... 处理函数
|->&CCmdTarget::messageMap
|->&CCmdTarget::_messageEntries[ ]
|->消息ID ... 处理函数
|->NULL
三、执行过程
1.获取和窗口句柄绑定的框架类对象pFrame
2.利用pFrame调用宏展开的虚函数GetMessageMap获取本类静态变量地址(链表头结点)pMessageMap
3.利用pMessageMap(静态变量)的第二个成员获取相应类的静态数组并到数组中匹配查找消息ID和对应的处理函数
如果找到,执行5
4.如果没找,用pMessageMap的第一个成员获取父类静态变量地址,如果为NULL,结束查找;如果不为NULL,执行3
5.用找到的数组元素的第六个成员保存的函数地址并调用,完成消息处理
伪代码:
//以WM_CREATE消息为例(同时注意WM_COMMAND / WM_PAINT)
AfxWndProc( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam )
{
CWnd* pWnd = CWnd::FromHandlePermanent( hWnd ); //获取和窗口句柄绑定的框架类对象pFrame
return AfxCallWndProc( pWnd, hWnd, nMsg, wParam, lParam ) //参数的pWnd就是pFrame
{
lResult = pWnd->WindowProc( nMsg, wParam, lParam ) //函数内部this为pFrame
{
LRESULT lResult = 0;
OnWndMsg( message, wParam, lParam, &lResult ) //函数内部this为pFrame
{
union MessageMapFunctions mmf;
AFX_MSGMAP* pMessageMap = GetMessageMap( ) //获得链表头结点
{
return GetThisMessageMap( ); //返回链表头结点地址
}
AFX_MSGMAP_ENTRY* lpEntry;
//遍历链表
for ( ; pMessageMap->pfnGetBaseMap != NULL; pMessageMap = ( *pMessageMap->pfnGetBaseMap)( ) )
{
lpEntry = AfxFindMessageEntry( pMessageMap->lpEntries, message, 0, 0 ) );
if ( lpEntry != NULL )
{
goto LDispatch; //找到跳出for循环
}
}
LDispatch:
mmf.pfn = lpEntry->pfn; //将OnCreate保存到联合体的所有成员中
switch( lpEntry->nSig )
{
case AfxSig_l_w_l:
lResult = ( this->*mmf.pfn_l_w_l )( wParam, lParam );
break;
}
}
}
}
}
相关代码:
#include "stdafx.h" #include "T10_MFCMsg.h" class CMyFrameWnd : public CFrameWnd { DECLARE_MESSAGE_MAP() /* protected: static const AFX_MSGMAP* PASCAL GetThisMessageMap(); virtual const AFX_MSGMAP* GetMessageMap() const; */ public: LRESULT OnCreate(WPARAM wParam, LPARAM lParam); LRESULT OnPaint(WPARAM wParam, LPARAM lParam); LRESULT OnMouseMove(WPARAM wParam, LPARAM lParam); private: int m_x; int m_y; public: CMyFrameWnd(); }; BEGIN_MESSAGE_MAP( CMyFrameWnd, CFrameWnd ) ON_MESSAGE(WM_CREATE, OnCreate) ON_MESSAGE(WM_PAINT, OnPaint) ON_MESSAGE(WM_MOUSEMOVE, OnMouseMove) END_MESSAGE_MAP() /* const AFX_MSGMAP* CMyFrameWnd::GetMessageMap() const { return GetThisMessageMap(); } const AFX_MSGMAP* PASCAL CMyFrameWnd::GetThisMessageMap() { typedef CMyFrameWnd ThisClass; typedef CFrameWnd TheBaseClass; static const AFX_MSGMAP_ENTRY _messageEntries[] = { { WM_CREATE, 0, 0, 0, AfxSig_lwl, (AFX_PMSG)(AFX_PMSGW) (static_cast<LRESULT(AFX_MSG_CALL CWnd::*)(WPARAM, LPARAM)> (&OnCreate)) }, { 0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } }; static const AFX_MSGMAP messageMap = { &TheBaseClass::GetThisMessageMap, &_messageEntries[0] }; return &messageMap; } */ CMyFrameWnd::CMyFrameWnd() { m_x = 100; m_y = 100; } LRESULT CMyFrameWnd::OnMouseMove(WPARAM wParam, LPARAM lParam) { m_x = LOWORD(lParam); m_y = HIWORD(lParam); ::InvalidateRect(m_hWnd, NULL, TRUE); return 0; } LRESULT CMyFrameWnd::OnPaint(WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps = { 0 }; HDC hdc = ::BeginPaint(m_hWnd, &ps); ::TextOut(hdc, m_x, m_y, L"hello", 5); ::EndPaint(m_hWnd, &ps); return 0; } LRESULT CMyFrameWnd::OnCreate(WPARAM wParam, LPARAM lParam) { AfxMessageBox(L"CMyFrameWnd::OnCreate"); return 0; } class CMyWinApp : public CWinApp { public: virtual BOOL InitInstance(); }; CMyWinApp theApp; BOOL CMyWinApp::InitInstance() { CMyFrameWnd *pFrame = new CMyFrameWnd; pFrame->Create(NULL, L"MFCMsg"); m_pMainWnd = pFrame; pFrame->ShowWindow(SW_SHOW); pFrame->UpdateWindow(); return TRUE; }
运行结果:
四、MFC消息分类
1.windows的标准消息(键盘/鼠标/定时器...)
ON_WM_XXX
ON_WM_CREATE
ON_WM_PAINT
ON_WM_MOUSEMOVE
......
2.自定义消息
#define WM_MYMESSAGE WM_USER + n(1 - 31743)
消息宏:ON_MESSAGE
3.命令消息(WM_COMMAND)
消息宏:ON_COMMAND
4.通知消息(WM_COMMAND)
消息宏:ON_通知码 例如:ON_EN_CHANGE / ON_BN_CLICKED
相关代码:
#include "stdafx.h" #include "T11_MFCCmd.h" #define WM_MYMESSAGE WM_USER+1001 class CMyFrameWnd : public CFrameWnd { DECLARE_MESSAGE_MAP() public: afx_msg int OnCreate(LPCREATESTRUCT pcs); afx_msg void OnPaint(); afx_msg LRESULT OnMyMsg(WPARAM wParame, LPARAM lParam); afx_msg void OnOk(); afx_msg void OnEnChange(); afx_msg void OnBnClick(); }; BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd) ON_WM_CREATE() //基本消息 ON_WM_PAINT() ON_MESSAGE(WM_MYMESSAGE, OnMyMsg) //自定义消息 //ON_COMMAND(1001, OnOk) //命令消息 ON_EN_CHANGE(1002, OnEnChange) //通知消息 ON_BN_CLICKED(1001, OnBnClick) END_MESSAGE_MAP() void CMyFrameWnd::OnBnClick() { AfxMessageBox(L"OK按钮被点击"); } void CMyFrameWnd::OnEnChange() { AfxMessageBox(L"内容被修改"); } void CMyFrameWnd::OnOk() { AfxMessageBox(L"OK按钮被点击"); } LRESULT CMyFrameWnd::OnMyMsg(WPARAM wParame, LPARAM lParam) { AfxMessageBox(L"处理自己定义的消息"); return 0; } void CMyFrameWnd::OnPaint() { PAINTSTRUCT ps = { 0 }; HDC hdc = ::BeginPaint(m_hWnd, &ps); ::TextOut(hdc, 100, 100, L"hello", 5); ::EndPaint(m_hWnd, &ps); } int CMyFrameWnd::OnCreate(LPCREATESTRUCT pcs) { ::CreateWindowEx(0, L"BUTTON", L"OK", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 100, 100, 100, 40, m_hWnd, (HMENU)1001, AfxGetInstanceHandle(), NULL); ::CreateWindowEx(0, L"EDIT", L"", WS_BORDER | WS_VISIBLE | WS_CHILD, 400, 100, 100, 100, m_hWnd, (HMENU)1002, AfxGetInstanceHandle(), NULL); AfxMessageBox(L"CMyFrameWnd::OnCreate"); ::PostMessage(m_hWnd, WM_MYMESSAGE, 1, 2); return CFrameWnd::OnCreate(pcs); } class CMyWinApp : public CWinApp { public: virtual BOOL InitInstance(); }; CMyWinApp theApp; BOOL CMyWinApp::InitInstance() { CMyFrameWnd *pFrame = new CMyFrameWnd; m_pMainWnd = pFrame; pFrame->Create(NULL, L"MFCCmd"); pFrame->ShowWindow(SW_SHOW); pFrame->UpdateWindow(); return TRUE; }
运行结果: