MFC 消息映射、分派和传递

几个重要的结构体:

struct AFX_MSGMAP
{
      AFX_MSGMAP* pBaseMessageMap;
      AFX_MSGMAP_ENTRY* lpEntries;
}

 

struct AFX_MSGMAP_ENTRY
{
    UINT nMessage;// Windows消息
    UINT nCode;// 控制消息的通知码
    UINT nID;// 其控制组件的ID
    UINT nLastID;// 如果是一定范围的消息映射,此值表示区间的最大值
    UINT nSig;  // 消息的动作标识
    AFX_MSG pfn;// 消息响应函数
}

此结构体的数据由任何一个ON_宏都会把这六个数据初始化,例如 此处的ON_WM_CREATE 和 下面提到的ON_COMMAND:

#define ON_WM_CREATE() \
{ WM_CREATE, 0, 0, 0, AfxSig_is, (AFX_MSG)(AFX_PMSGW)(int (AFX_MSG_CALL CWnd::*)(LPCREATESTRUCT)OnCreate) },

  

AFX_PMSG 是一个函数指针:

typedef void (CCmdTarget::* AFX_PMSG)(void);

定义一个DECLARE_MESSAGE_MAP()宏

#define DECLARE_MESSAGE_MAP() \
   static AFX_MSGMAP_ENTRY _messageEntries[] ;\
   static AFX_MSGMAP messageMap; \
   virtual AFX_MSGMAP* GetMessageMap() const;

于是DECLARE_MESSAGE_MAP宏就相当于声明了下面的数据结构:

  

  

 这个数据结构的内容填充工作由下面三个宏完成:

#define BEGIN_MESSAGE_MAP (theClass,baseClass) \
   AFX_MSGMAP* theClass::GetMessageMap() const \
                           { return &theClass::messageMap; }\
   AFX_MSGMAP theClass::messageMap = \
   {
        &(baseClass::messageMap), \
        (AFX_MSGMAP_ENTRY*) &(theClass::_messageEntries) ; \
   } \
   AFX_MSGMAP_ENTRY  theClass::_messageEntries[] = \
   {
     
#define ON_COMMAND(id,memberFxn) \ 
{ WM_COMMAND, 0, (WORD)id, (WORD)id, AfxSig_vv, (AFX_PMSG)memberFxn },
#define END_MESSAGE_MAP() \
  { 0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0} \
};

这个AfxSig枚举的具体作用是什么呢?

上面的宏ON_WM_CREATE和ON_COMMAND也有出现类似的AfxSig_,那么到底作何用处?

从上面两个宏的最后一个参数可以看出,表示的是具体的执行函数,且都进行了类型转换,最终转换为AFX_PMSG.

可是AFX_PMSG的定义呢?我们再写一遍。

typedef void (CCmdTarget::* AFX_PMSG)(void);

这是一个什么鬼?无参,无返回值,不可能所有的函数都如此吧?

这正是AfxSig_的作用所在,当需要调用AFX_MSGMAP_ENTRY中的消息响应函数pfn的时候,具体的动作是这样的(出现在wincore.cpp CWnd::OnWndMsg 和 DispatchCmdMsg中):

union MessageMapFunctions mmf;
mmf.pfn = lpEntry->pfn; // lpEntry 是AFX_MSGMAP_ENTRY的对象
switch(lpEntry->nSig)
{
    case AfxSig_is:
             lResult = (this->*mmf.pfn_is)((LPTSTR)lParam);
             break;
    case AfxSig_lwl:
             lResult = (this->*mmf.pfn_lwl)(wParam, lParam);
             break;
    case AfxSig_vv:
             lResult = (this->*mmf.pfn_vv)();
             break;
    .....
}

重点注意: MessageMapFunctions 和AfxSig_. AfxSig_ 定义位于AFXMSG_.H档。

enum AfxSig
{
	AfxSig_end = 0,     // [marks end of message map]

	AfxSig_b_D_v,				// BOOL (CDC*)
	AfxSig_b_b_v,				// BOOL (BOOL)
	AfxSig_b_u_v,				// BOOL (UINT)
	AfxSig_b_h_v,				// BOOL (HANDLE)
	AfxSig_b_W_uu,				// BOOL (CWnd*, UINT, UINT)
	AfxSig_b_W_COPYDATASTRUCT,				// BOOL (CWnd*, COPYDATASTRUCT*)
	AfxSig_b_v_HELPINFO,		// BOOL (LPHELPINFO);
	AfxSig_CTLCOLOR,			// HBRUSH (CDC*, CWnd*, UINT)
	AfxSig_CTLCOLOR_REFLECT,	// HBRUSH (CDC*, UINT)
	AfxSig_i_u_W_u,				// int (UINT, CWnd*, UINT)  // ?TOITEM
	AfxSig_i_uu_v,				// int (UINT, UINT)
	AfxSig_i_W_uu,				// int (CWnd*, UINT, UINT)
	AfxSig_i_v_s,				// int (LPTSTR)
	AfxSig_l_w_l,				// LRESULT (WPARAM, LPARAM)
	AfxSig_l_uu_M,				// LRESULT (UINT, UINT, CMenu*)
	....  // 后面很多不列举
};

 

MessageMapFunctions 定义于WINCORE.cpp 中:

union MessageMapFunctions
{
	AFX_PMSG pfn;   // generic member function pointer

	BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_D)(CDC*);
	BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_b)(BOOL);
	BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_u)(UINT);
	BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_h)(HANDLE);
	BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_W_u_u)(CWnd*, UINT, UINT);
	BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_W_COPYDATASTRUCT)(CWnd*, COPYDATASTRUCT*);
	BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_HELPINFO)(LPHELPINFO);
	HBRUSH (AFX_MSG_CALL CCmdTarget::*pfn_B_D_W_u)(CDC*, CWnd*, UINT);
	HBRUSH (AFX_MSG_CALL CCmdTarget::*pfn_B_D_u)(CDC*, UINT);
	int (AFX_MSG_CALL CCmdTarget::*pfn_i_u_W_u)(UINT, CWnd*, UINT);
	int (AFX_MSG_CALL CCmdTarget::*pfn_i_u_u)(UINT, UINT);
	int (AFX_MSG_CALL CCmdTarget::*pfn_i_W_u_u)(CWnd*, UINT, UINT);
	int (AFX_MSG_CALL CWnd::*pfn_i_s)(LPTSTR);
	int (AFX_MSG_CALL CWnd::*pfn_i_S)(LPCTSTR);
	LRESULT (AFX_MSG_CALL CWnd::*pfn_l_w_l)(WPARAM, LPARAM);
	LRESULT (AFX_MSG_CALL CWnd::*pfn_l_u_u_M)(UINT, UINT, CMenu*);
	....  // 后面很多不列举
};

 

其实真正的函数只有pfn一个,但通过union后,它就有了许多类型不同的形象。这里可以看出union自动转型的作用。  

//***********************************************************************************************

上面讲基本内容解释完毕,下面以实例来演示宏展开后的结果:

//in header file
class CView : public CWnd
{
public:
   //...
	DECLARE_MESSAGE_MAP()
};

//in implementation file
#define CViewid 122
//...
BEGIN_MESSAGE_MAP(CView, CWnd)
	ON_COMMAND(CViewid, 0)
END_MESSAGE_MAP

//被展开之后为

//in header file
class CView : public CWnd
{
public:
	//...
	static AFX_MSGMAP_ENTRY _messageEntries[];
	static AFX_MSGMAP messageMap;
	virtual AFX_MSGMAP* GetMessageMap() const;
};

//in implementation file
AFX_MSGMAP* CView::GetMessageMap() const
{
	return &CView::messageMap;
}

AFX_MSGMAP CView::messageMap = { &(CWnd::messageMap), (AFX_MSGMAP_ENTRY*)&(CView::_messageEntries) };

AFX_MSGMAP_ENTRY CView::_messageEntries[] = { 
     {WM_COMMAND, 0, (WORD)122, (WORD)122, 1, (AFX_MSGMAP)0},
     {0, 0, 0, 0, 0, (AFX_MSGMAP)0}
};

以图表示则为:

  

MFC中定义了各种类似ON_COMMAND这样的宏,把各式各样的消息与特定的消息处理函数关联起来。

 

下面的代码中每一个CCmdTarget的衍生类都产生类似上图的消息映射表:

//in head file
class CObject
{
	//....
	//注意:CObject并不属于消息流动网的一份子
};

class CCmdTarget :public CObject
{
	//...
	DECLARE_MESSAGE_MAP()
};

class CWinThread :public CCmdTarget
{
	//...
	//注意:CWinThread并不属于消息流动网的一份子
};

class CWinApp :public CWinThread
{
	//...
	DECLARE_MESSAGE_MAP()
};

class CDocument :public CCmdTarget
{
	//...
	DECLARE_MESSAGE_MAP()
};

class CWnd :public CCmdTarget
{
	//...
	DECLARE_MESSAGE_MAP()
};

class CFrameWnd :public CWnd
{
	//...
	DECLARE_MESSAGE_MAP()
};

class CView :public CWnd
{
	//...
	DECLARE_MESSAGE_MAP()
};

class CView :public CWnd
{
	//...
	DECLARE_MESSAGE_MAP()
};

class CMyWinApp :public CWinApp
{
	//...
	DECLARE_MESSAGE_MAP()
};

class CMyFrameWnd :public CFrameWnd
{
	//...
	DECLARE_MESSAGE_MAP()
};

class CMyDoc :public CDocument
{
	//...
	DECLARE_MESSAGE_MAP()
};

class CMyView :public CView
{
	//...
	DECLARE_MESSAGE_MAP()
};
//in implementation file
BEGIN_MESSAGE_MAP(CWnd, CCmdTarget)
	ON_COMMAND(CWndid, 0)
END_MESSAGE_MAP

BEGIN_MESSAGE_MAP(CFrameWnd, CWnd)
	ON_COMMAND(CFrameWndid, 0)
END_MESSAGE_MAP()

BEGIN_MESSAGE_MAP(CDocument, CCmdTarget)
	ON_COMMAND(CDocumentid, 0)
END_MESSAGE_MAP()

BEGIN_MESSAGE_MAP(CView, CWnd)
	ON_COMMAND(CViewid, 0)
END_MESSAGE_MAP()

BEGIN_MESSAGE_MAP(CWinApp, CCmdTarget)
	ON_COMMAND(CWinAppid, 0)
END_MESSAGE_MAP()

BEGIN_MESSAGE_MAP(CMyWinApp, CWinApp)
	ON_COMMAND(CMyWinAppid, 0)
END_MESSAGE_MAP()

BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
	ON_COMMAND(CMyFrameWndid, 0)
END_MESSAGE_MAP()

BEGIN_MESSAGE_MAP(CMyDoc, CDocument)
	ON_COMMAND(CMyDocid, 0)
END_MESSAGE_MAP()

BEGIN_MESSAGE_MAP(CMyView, CView)
	ON_COMMAND(CMyViewid, 0)
END_MESSAGE_MAP()

//同时也设定了消息的终极标靶CCmdTarget的映射表内容:
AFX_MSGMAP CCmdTarget::messageMap =
{
	NULL,
	&CCmdTarget::_messageEntries[0]
};

AFX_MSGMAP_ENTRY CCmdTarget::_messageEntries =
{
	{0, 0, CCmdTargetid, 0, AfxSig_end, 0 }
}

以上构成了完整的消息流动网,如下图:

 

posted @ 2016-01-31 20:14  米罗西  阅读(658)  评论(0编辑  收藏  举报