---- 二:我们可以直接重载DefWindowProc方法,对message类型等于WM_CHAR的,直接修改nChar的值即可。 4.小结
---- 通过对MFC类库的分析和了解,不仅能够使我们更好的使用MFC类库,同时,对于我们自己设计和实现框架和类,无疑也有相当大的帮助。
二.MFC的消息映射机制
MFC的设计者们在设计MFC时,紧紧把握一个目标,那就是尽可能使得MFC的代码要小,速度尽可能快。为了这个目标,他们使用了许多技巧,其中很多技巧体现在宏的运用上,实现MFC的消息映射的机制就是其中之一。
同MFC消息映射机制有关的宏有下面几个: DECLARE_MESSAGE_MAP()宏
BEGIN_MESSAGE_MAP(theClass, baseClass)和END_MESSAGE_MAP()宏
弄懂MFC消息映射机制的最好办法是将找出一个具体的实例,将这些宏展开,并找出相关的数据结构。 DECLARE_MESSAGE_MAP()
DECLARE_MESSAGE_MAP()宏的定义如下: #define DECLARE_MESSAGE_MAP() \\ private: \\
static const AFX_MSGMAP_ENTRY _messageEntries[]; \\ protected: \\
static AFX_DATA const AFX_MSGMAP messageMap; \\ virtual const AFX_MSGMAP* GetMessageMap() const; \\ 从上面的定义可以看出,DECLARE_MESSAGE_MAP()作下面三件事: 定义一个长度不定的静态数组变量_messageEntries[]; 定义一个静态变量messageMap; 定义一个虚拟函数GetMessageMap();
在DECLARE_MESSAGE_MAP()宏中,涉及到MFC中两个对外不公开的数据结构
AFX_MSGMAP_ENTRY和AFX_MSGMAP。为了弄清楚消息映射,有必要考察一下这两个数据结构的定义。 AFX_MSGMAP_ENTRY的定义 struct AFX_MSGMAP_ENTRY {
UINT nMessage; // windows message
UINT nCode; // control code or WM_NOTIFY code UINT nID; // control ID (or 0 for windows messages)
UINT nLastID; // used for entries specifying a range of control id's UINT nSig; // signature type (action) or pointer to message # AFX_PMSG pfn; // routine to call (or special value) };
结构中各项的含义注释已经说明得很清楚了,这里不再多述,从上面的定义你是否看出,AFX_MSGMAP_ENTRY结构实际上定义了消息和处理此消息的动作之间的映射关系。因此静态数组变量_messageEntries[]实际上定义了一张表,表中的每一项指定了相应的对象所要处理的消息和处理此消息的函数的对应关系,因而这张表也称为消息映射表。再看看AFX_MSGMAP的定义。 (2)AFX_MSGMAP的定义
struct AFX_MSGMAP {
const AFX_MSGMAP* pBaseMap; const AFX_MSGMAP_ENTRY* lpEntries; };
不难看出,AFX_MSGMAP定义了一单向链表,链表中每一项的值是一指向消息映射表的指针(实际上就是_messageEntries的值)。通过这个链表,使得在某个类中调用基类的的消息处理函数很容易,因此,“父类的消息处理函数是子类的缺省消息处理函数”就“顺理成章”了。在后面的“MFC窗口的消息处理”一节中会对此作详细的讲解。
由上述可见,在类的头文件中主要定义了两个数据结构:消息映射表和单向链表。(孙建东总结) BEGIN_MESSAGE_MAP()和END_MESSAGE_MAP() 它们的定义如下:
#define BEGIN_MESSAGE_MAP(theClass, baseClass) \\ const AFX_MSGMAP* theClass::GetMessageMap() const \\ { return &theClass::messageMap; } \\
AFX_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap = \\ { &baseClass::messageMap, &theClass::_messageEntries[0] }; \\ AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \\ { \\
#define END_MESSAGE_MAP() \\
{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \\ }; \\
对应BEGIN_MESSAGE_MAP()的定义可能不是一下子就看得明白,不过不要紧,举一例子就很清楚了。对于BEGIN_MESSAGE_MAP(CView, CWnd),VC预编译器将其展开成下面的形式: const AFX_MSGMAP* CView::GetMessageMap() const {
return &CView::messageMap; }
AFX_COMDAT AFX_DATADEF const AFX_MSGMAP CView::messageMap = {
&CWnd::messageMap,
&CView::_messageEntries[0] };
AFX_COMDAT const AFX_MSGMAP_ENTRY CView::_messageEntries[] = {
至于END_MESSAGE_MAP()则不过定义了一个表示映射表结束的标志项,我想大家对于这种简单的技巧应该是很熟悉的,无需多述。
到此为止,我想大家也已经想到了象ON_COMMAND这样的宏的具体作用了,不错它们只不过定义了一种类型的消息映射项,看看ON_COMMAND的定义: #define ON_COMMAND(id, memberFxn) \\
{ WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSig_vv, (AFX_PMSG)&memberFxn }, 根据上面的定义,ON_COMMAND(ID_FILE_NEW, OnFileNew)将被VC预编译器展开 如下:
{WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSig_vv,
(AFX_PMSG)&OnFileNew},
到此,MFC的消息映射机制已经清楚了,现在提出并解答两个问题以作为对这一节的小结。
为什么不直接使用虚拟函数实现消息处理函数呢?这是一个GOOD QUESTION。前面已经说过,MFC的设计者们在设计MFC时有一个很明确的目标,就是使得“MFC的代码尽可能小,速度尽可能快”,如果采用虚拟函数,那么对于所有的窗口消息,都必须有一个与之对应的虚拟函数,因而对每一个从CWnd派生的类而言,都会有一张很大的虚拟函数表vtbl。但是在实际应用中,一般只对少数的消息进行处理,大部分都交给系统缺省处理,所以表中的大部分项都是无用项,这样做就浪费了很多内存资源,这同MFC设计者们的设计目标是相违背的。当然,MFC所使用的方法只是解决这类问题的方式之一,不排除还有其他的解决方式,但就我个人观点而言,这是一种最好的解决方式,体现了很高的技巧性,值得我们学习。
至于这第二个问题,是由上面的问题引申出来的。如果在子类和父类中出现了相同的消息出来函数,VC编译器会怎么处理这个问题呢?VC不会将它们看作错误,而会象对待虚拟函数类似的方式去处理,但对于消息处理函数(带afx_msg前缀),则不会生成虚拟函数表vtbl。 MFC下一个消息的处理过程是一般是这样的。
1、_AfxCbtFilterHook截获消息(这是一个钩子函数) 2、_AfxCbtFilterHook把窗口过程设定为AfxWndProc。 3、函数AfxWndProc接收Windows操作系统发送的消息。 4、函数AfxWndProc调用函数AfxCallWndProc进行消息处理。
5、函数AfxCallWndProc调用CWnd类的方法WindowProc进行消息处理。
如何添加自己的消息?
我们已经了解了WINDOW的消息机制,如何加入我们自己的消息呢?好我们来看 一个标准的消息处理程序是这个样子的
在 CWnd 类中预定义了标准 Windows 消息 (WM_XXXX WM是WINDOW MESSAGE的缩写) 的默认处理程序。类库基于消息名命名这些处理程序。例如,WM_PAINT 消息的处理程序在 CWnd 中被声明为: afx_msg void OnPaint();
afx_msg 关键字通过使这些处理程序区别于其他 CWnd 成员函数来表明 C++ virtual 关键字的作用。但是请注意,这些函数实际上并不是虚拟的,而是通过消息映射实现的。我们在本文的一开始便说明了为什么要这样做。
所有能够进行消息处理的类都是基于CCmdTarget类的,也就是说CCmdTarget类是所有可以进行消息处理类的父类。CCmdTarget类是MFC处理命令消息的基础和核心。
若要重写基类中定义的处理程序,只需在派生类中定义一个具有相同原型的函数,并创建此处理程序的消息映射项。我们通过ClassWizard可以建立大多数窗口消息或自定义的消息,通过ClassWizard可以自动建立消息映射,和消息处理函数的框架,我们只需要把我们要做的事情填空,添加你要做的事情到处理函数。这个非常简单,就不细说了。但是也许我们需要添加一些ClassWizard不支持的窗口消息或自定义消息,那么就需要我们亲自动手建立消息映射和消息处理的框架,通常步骤如下:
第一步:定义消息。Microsoft推荐用户自定义消息至少是WM_USER+100,因为很多新控件也要使用WM_USER消息。
#define WM_MYMESSAGE (WM_USER + 100)
第二步:实现消息处理函数。该函数使用WPRAM和LPARAM参数并返回LPESULT。 LPESULT CMainFrame::OnMyMessage(WPARAM wParam, LPARAM lParam)
{
// TODO: 处理用户自定义消息,填空就是要填到这里。 return 0; }
第三步:在类头文件的AFX_MSG块中说明消息处理函数: // {{AFX_MSG(CMainFrame)
afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam); //}}AFX_MSG
DECLARE_MESSAGE_MAP()
第四步:在用户类的消息块中,使用ON_MESSAGE宏指令将消息映射到消息处理函数中。 ON_MESSAGE( WM_MYMESSAGE, OnMyMessage )
MPW内部网络是一个邀请最有影响力的商界女性及时解答职业与领导力问题的在线社区。本周,我们的问题是:千禧一代在进入职场之前应该听从什么建议?以下是 MPW Insider is one of several online communities where the biggest names in business answer timely career and leadership questions. Today’s answer for:
Hearsay Social创始人兼CEO史宗玮的回答。 What is one piece of advice all millennials
should
80后和90后们,要知道,你们这个
take
before
entering
the
workforce? is written by Clara Shih,
年纪是进入职场最好的时段。抓紧这段时间,founder and CEO of Hearsay Social. 尽最大可能地去学习和做事,未来你肯定会感激自己今天的付出的。
回顾我的第一份工作,即便我被要求去做一些看似很卑微、单调或者非常困难的事情时,我也会全力以赴。有的上司是我最不喜欢的类型,但即便如此,我依旧没有放弃、抱怨或懈怠。我会加班到很晚,如果有必要还会通宵达旦地工作,我学会了只有在自己确实无法解决的时候才去寻求帮助;只要是对我的上司和团队有利的事,我都会主动承担。
一旦能够游刃有余地完成上司安排的工作,我就会挑战自己的能力极限。我会不断问自己:“我还能做什么?我还能做些什么让这件事变得更好?”这种心态让我在职业初期便拥有了更开阔的思路,主动向团队提出一些新的想法——虽然并非所有想法都是好的或者被采纳,但总能获得团队的欣赏。最后,“全力以赴”的工作劲头,让我获得上司和同事的尊重。我有幸加入战略对话,这让我有机会了解全局,承担更大的职责。
如果你局限于短期思考,你或许认为这种想法有违常规,但如果你希望在职业
Once I got good at what was asked of me, I challenged myself to go further. I continually asked myself, ―What else can I do? How else can I make this better?‖ This mindset led me early in my career to think bigger and proactively suggest new ideas to the team–certainly not all of them good or accepted, but almost always appreciated. Ultimately, going ―all in‖ won Looking back at my first job, even when I was asked to do something seemingly menial, unglamorous, or very difficult, I always went all in. In my most trying moments with managers I liked the least, I did not give up, complain, or slack off. I stayed late, pulled all-nighters when necessary, learned to ask for help only when I couldn’t figure it out myself, and basically did whatever it took to make my manager and team look good.
Millennials, you could not be entering the workforce at a more exciting time. Seize the day, learn and do the most you possibly can today, and your future self