【转】MFC六大关键技术之(五)(六)——消息映射与命令传递

danxuezx 2009-06-08 05:20:10
MFC六大关键技术之(五)(六)——消息映射与命令传递收藏
MFC六大关键技术之(五)(六)——消息映射与命令传递

题外话:刚开始学视窗程序设计的时候,我就打印了一本Windows消息详解,里面列举了各种已定义消息的意义和作用,共10多页,在编程的时候翻翻,有时觉得很受用。我发觉很多编程的朋友,虽然每天都面对消息,却很少关注它。C++程序员有一个通病,很想写“自己”的程序,即每一行代码都想自己写出来。如果用了一些库,总希望能完全理解库里的类或函数是怎么一回事,否则就“不踏实”。对于消息,许多朋友只关心常用的几个,对其余的漠不关心。其实,Windows中有很多不常用的消息却很有用,程序员可能通过响应这些消息实现更简捷的编程。

说到消息,在MFC中,“最熟悉的神秘”可算是消息映射,那是我们刚开始接触MFC时就要面对的东西。有过SDK编程经验的朋友转到MFC编程的时候,一下子觉得什么都变了样。特别是窗口消息及对消息的处理跟以前相比,更是风马牛不相及的。如文档不是窗口,是怎样响应命令消息的呢?

初次用MFC编程,我们只会用MFC ClassWizard为我们做大量的东西,最主要的是添加消息响应。记忆中,如果是自已添加消息响应,我们应何等的小心翼翼,对BEGIN_MESSAGE_MAP()……END_MESSAGE_MAP()更要奉若神灵。它就是一个魔盒子,把我们的咒语放入恰当的地方,就会发生神奇的力量,放错了,自己的程序就连“命”都没有。

据说,知道得太多未必是好事。我也曾经打算不去理解这神秘的区域,觉得编程的时候知道自己想做什么就行了。MFC外表上给我们提供了东西,直观地说,不但给了我个一个程序的外壳,更给我们许多方便。微软的出发点可能是希望达到“傻瓜编程”的结果,试想,谁不会用ClassWizard?大家知道,Windows是基于消息的,有了ClassWizard,你又会添加类,又会添加消息,那么你所学的东西似乎学到头了。于是许多程序员认为“我们没有必要走SDK的老路,直接用MFC编程,新的东西通常是简单、直观、易学……”

到你真正想用MFC编程的时候,你会发觉光会ClassWizard的你是多么的愚蠢。MFC不是一个普通的类库,普通的类库我们完全可以不理解里面的细节,只要知道这些类库能干什么,接口参数如何就万事大吉。如string类,操作顺序是定义一个string对象,然后修改属性,调用方法。

但对于MFC,你并不是在你的程序中写上一句“#include MFC.h”,然后就在你的程序中用MFC类库。

MFC是一块包着糖衣的牛骨头。你很轻松地写出一个单文档窗口,在窗口中间打印一句“I love MFC!”,然后,恶梦开始了……想逃避,打算永远不去理解MFC内幕?门都没有!在MFC这个黑暗神秘的洞中,即使你打算摸着石头前行,也注定找不到出口。对着MFC这块牛骨头,微软温和、民主地告诉你“你当然可以选择不啃掉它,咳咳……但你必然会因此而饿死!”

消息映射与命令传递体现了MFC与SDK的不同。在SDK编程中,没有消息映射的概念,它有明确的回调函数中,通过一个switch语句去判断收到了何种消息,然后对这个消息进行处理。所以,在SDK编程中,会发送消息和在回调函数中处理消息就差不多可以写SDK程序了。

在MFC中,看上去发送消息和处理消息比SDK更简单、直接,但可惜不直观。举个简单的例子,如果我们想自定义一个消息,SDK是非常简单直观的,用一条语句:SendMessage(hwnd,message/*一个大于或等于WM_USER的数字*/,wparam,lparam),之后就可以在回调函数中处理了。但MFC就不同了,因为你通常不直接去改写窗口的回调函数,所以只能亦步亦趋对照原来的MFC代码,把消息放到恰当的地方。这确实是一样很痛苦的劳动。

要了解MFC消息映射原理并不是一件轻松的事情。我们可以逆向思维,想象一下消息映射为我们做了什么工作。MFC在自动化给我们提供了很大的方便,比如,所有的MFC窗口都使用同一窗口过程,即所有的MFC窗口都有一个默认的窗口过程。不象在SDK编程中,要为每个窗口类写一个窗口过程。

对于消息映射,最直截了当地猜想是:消息映射就是用一个数据结构把“消息”与“响应消息函数名”串联起来。这样,当窗口感知消息发生时,就对结构查找,找到相应的消息响应函数执行。其实这个想法也不能简单地实现:我们每个不同的MFC窗口类,对同一种消息,有不同的响应方式。即是说,对同一种消息,不同的MFC窗口会有不同的消息响应函数。

这时,大家又想了一个可行的方法。我们设计窗口基类(CWnd)时,我们让它对每种不同的消息都来一个消息响应,并把这个消息响应函数定义为空虚函数。这样,从CWnd派生的窗口类对所有消息都有了一个空响应,我们要响应一个特定的消息就重载这个消息响应函数就可以了。但这样做的结果,一个几乎什么也不做的CWnd类要有几百个“多余”的函数,那怕这些消息响应函数都为纯虚函数,每个CWnd对象也要背负着一个巨大的虚拟表,这也是得不偿失的。

许多朋友在学习消息映射时苦无突破,其原因是一开始就认为MFC的消息映射的目的是为了替代SDK窗口过程的编写——这本来没有理解错。但他们还有多一层的理解,认为既然是替代“旧”的东西,那么MFC消息映身应该是更高层次的抽象、更简单、更容易认识。但结果是,如果我们不通过ClassWizard工具,手动添加消息是相当迷茫的一件事。

所以,我们在学习MFC消息映射时,首先要弄清楚:消息映射的目的,不是为是更加快捷地向窗口过程添加代码,而是一种机制的改变。如果不想改变窗口过程函数,那么应该在哪里进行消息响应呢?许多朋友一知半解地认为:我们可以用HOOK技术,抢在消息队列前把消息抓取,把消息响应提到窗口过程的外面。再者,不同的窗口,会有不同的感兴趣的消息,所以每个MFC窗口都应该有一个表把感兴趣的消息和相应消息响应函数连系起来。然后得出——消息映射机制执行步骤是:当消息发生,我们用HOOK技术把本发送到窗口过程的消息抓获,然后对照一下MFC窗口的消息映射表,如果是表里面有的消息,就执行其对应的函数。

当然,用HOOK技术,我们理论上可以在不改变窗口过程函数的情况下,可以完成消息响应。MFC确实是这样做,但实际操作起来可能跟你的想象差别很大。

现在我们来编写消息映射表,我们先定义一个结构,这个结构至少有两个项:一是消息ID,二是响应该消息的函数。如下:

struct AFX_MSGMAP_ENTRY

{

UINT nMessage; //感兴趣的消息

AFX_PMSG pfn; //响应以上消息的函数指针

}

当然,只有两个成员的结构连接起来的消息映射表是不成熟的。Windows消息分为标准消息、控件消息和命令消息,每类型的消息包含数百不同ID、不同意义、不同参数的消息。我们要准确地判别发生了何种消息,必须再增加几个成员。还有,对于AFX_PMSG pfn,实际上等于作以下声明:

void (CCmdTarget::*pfn)();

(提示:AFX_PMSG为类型标识,具体声明是:typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);)

pfn是不一不带参数和返回值的CCmdTarget类型函数指针,只能指向CCmdTarget类中不带参数和返回值的成员函数,这样pfn更为通用,但我们响应消息的函数许多需要传入参数的。为了解决这个矛盾,我们还要增加一个表示参数类型的成员。当然,还有其它……

最后,MFC我们消息映射表成员结构如下定义:

struct AFX_MSGMAP_ENTRY

{

UINT nMessage; //Windows 消息ID

UINT nCode; // 控制消息的通知码

UINT nID; //命令消息ID范围的起始值

UINT nLastID; //命令消息ID范围的终点

UINT nSig; // 消息的动作标识

AFX_PMSG pfn;

};

有了以上消息映射表成员结构,我们就可以定义一个AFX_MSGMAP_ENTRY类型的数组,用来容纳消息映射项。定义如下:

AFX_MSGMAP_ENTRY _messageEntries[];

但这样还不够,每个AFX_MSGMAP_ENTRY数组,只能保存着当前类感兴趣的消息,而这仅仅是我们想处理的消息中的一部分。对于一个MFC程序,一般有多个窗口类,里面都应该有一个AFX_MSGMAP_ENTRY数组。我们知道,MFC还有一个消息传递机制,可以把自己不处理的消息传送给别的类进行处理。为了能查找各下MFC对象的消息映射表,我们还要增加一个结构,把所有的AFX_MSGMAP_ENTRY数组串联起来。

于是,我们定义了一个新结构体:

struct AFX_MSGMAP

{

const AFX_MSGMAP* pBaseMap; //指向别的类的AFX_MSGMAP对象

const AFX_MSGMAP_ENTRY* lpEntries; //指向自身的消息表

};

之后,在每个打算响应消息的类中这样声明一个变量:AFX_MSGMAP messageMap,让其中的pBaseMap指向基类或另一个类的messageMap,那么将得到一个AFX_MSGMAP元素的单向链表。这样,所有的消息映射信息形成了一张消息网。

当然,仅有消息映射表还不够,它只能把各个MFC对象的消息、参数与相应的消息响应函数连成一张网。为了方便查找,MFC在上面的类中插入了两个函数(其中theClass代表当前类):

一个是_GetBaseMessageMap(),用来得到基类消息映射的函数。函数原型如下:

const AFX_MSGMAP* PASCAL theClass::_GetBaseMessageMap() \

{ return &baseClass::messageMap; } \

别一个是GetMessageMap() ,用来得到自身消息映射的函数。函数原型如下:

const AFX_MSGMAP* theClass::GetMessageMap() const \

{ return &theClass::messageMap; } \



有了消息映射表之后,我们得讨论到问题的关键,那就是消息发生以后,其对应的响应函数如何被调用。大家知道,所有的MFC窗口,都有一个同样的窗口过程——AfxWndProc(…)。在这里顺便要提一下的是,看过MFC源代码的朋友都得,从AfxWndProc函数进去,会遇到一大堆曲折与迷团,因为对于这个庞大的消息映射机制,MFC要做的事情很多,如优化消息,增强兼容性等,这一大量的工作,有些甚至用汇编语言来完成,对此,我们很难深究它。所以我们要省略大量代码,理性地分析它。

对已定型的AfxWndProc来说,对所有消息,最多只能提供一种默认的处理方式。这当然不是我们想要的。我们想通过AfxWndProc最终执行消息映射网中对应的函数。那么,这个执行路线是怎么样的呢?

从AfxWndProc下去,最终会调用到一个函数OnWndMsg。请看代码:

LRESULT CALLBACK AfxWndProc(HWND hWnd,UINT nMsg,WPARAM wParam, LPARAM lParam)

{

……

CWnd* pWnd = CWnd::FromHandlePermanent(hWnd); //把对句柄的操作转换成对CWnd对象。

Return AfxCallWndProc(pWnd,hWnd,nMsg,wParam,lParam);

}

把对句柄的操作转换成对CWnd对象是很重要的一件事,因为AfxWndProc只是一个全局函数,当然不知怎么样去处理各种windows窗口消息,所以它聪明地把处理权交给windows窗口所关联的MFC窗口对象。

现大,大家几乎可以想象得到AfxCallWndProc要做的事情,不错,它当中有一句:

pWnd->WindowProc(nMsg,wParam,lParam);

到此,MFC窗口过程函数变成了自己的一个成员函数。WindowProc是一个虚函数,我们甚至可以通过改写这个函数去响应不同的消息,当然,这是题外话。

WindowProc会调用到CWnd对象的另一个成员函数OnWndMsg,下面看看大概的函数原形是怎么样的:

BOOL CWnd::OnWndMsg(UINT message,WPARAM wParam,LPARAM lParam,LRESULT* pResult)

{

if(message==WM_COMMAND)

{

OnCommand(wParam,lParam);

……

}

if(message==WM_NOTIFY)

{

OnCommand(wParam,lParam,&lResult);

……

}

const AFX_MSGMAP* pMessageMap; pMessageMap=GetMessageMap();

const AFX_MSGMAP_ENTRY* lpEntry;

/*以下代码作用为:用AfxFindMessageEntry函数从消息入口pMessageMap处查找指定消息,如果找到,返回指定消息映射表成员的指针给lpEntry。然后执行该结构成员的pfn所指向的函数*/ if((lpEntry=AfxFindMessageEntry(pMessageMap->lpEntries,message,0,0)!=NULL)

{

lpEntry->pfn();/*注意:真正MFC代码中没有用这一条语句。上面提到,不同的消息参数代表不同的意义和不同的消息响应函数有不同类型的返回值。而pfn是一个不带参数的函数指针,所以真正的MFC代码中,要根据对象lpEntry的消息的动作标识nSig给消息处理函数传递参数类型。这个过程包含很复杂的宏代换,大家在此知道:找到匹配消息,执行相应函数就行!*/

}

}



以上,大家看到了OnWndMsg能根据传进来的消息参数,查找到匹配的消息和执行相应的消息响应。但这还不够,我们平常响应菜单命令消息的时候,原本属于框架窗口(CFrameWnd)的WM_COMMAND消息,却可以放到视对象或文档对象中去响应。其原理如下:

我们看上面函数OnWndMsg原型中看到以下代码:

if(message==WM_COMMAND)

{

OnCommand(wParam,lParam);

……

}

即对于命令消息,实际上是交给OnCommand函数处理。而OnCommand是一个虚函数,即WM_COMMAND消息发生时,最终是发生该消息所对应的MFC对象去执行OnCommand。比如点框架窗口菜单,即向CFrameWnd发送一个WM_COMMAND,将会导致CFrameWnd::OnCommand(wParam,lParam)的执行。



本文来自CSDN博客:http://blog.csdn.net/liyi268/archive/2006/03/13/623391.aspx
...全文
245 4 打赏 收藏 转发到动态 举报
写回复
用AI写文章
4 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复
路过,学习一下。
楼主写得辛苦,我们看得也辛苦。
晚上回去看再好好的看看。
njlengjiang 2009-06-08
  • 打赏
  • 举报
回复
谢谢楼主分享
danxuezx 2009-06-08
  • 打赏
  • 举报
回复
终于转贴完了,本来打算在一个帖子中贴出来,可是内容是在太长了。所以就分开来贴了。
非常感谢这6篇文章的作者。
danxuezx 2009-06-08
  • 打赏
  • 举报
回复
且看该函数原型

BOOL CFrameWnd::OnCommand(WPARAM wParam,LPARAM lParam)

{

……

return CWnd:: OnCommand(wParam,lParam);

}

可以看出,它最后把该消息交给CWnd:: OnCommand处理。再看:

BOOL CWnd::OnCommand(WPARAM wParam,LPARAM lParam)

{

……

return OnCmdMsg(nID,nCode,NULL,NULL);

}

这里包含了一个C++多态性很经典的问题。在这里,虽然是执行CWnd类的函数,但由于这个函数在CFrameWnd:: OnCmdMsg里执行,即当前指针是CFrameWnd类指针,再有OnCmdMsg是一个虚函数,所以如果CFrameWnd改写了OnCommand,程序会执行CFrameWnd::OnCmdMsg(…)。

对CFrameWnd::OnCmdMsg(…)函数原理扼要分析如下:

BOOL CFrameWnd:: OnCmdMsg(…)

{

CView pView = GetActiveView();//得到活动视指针。

if(pView-> OnCmdMsg(…))

return TRUE; //如果CView类对象或其派生类对象已经处理该消息,则返回。

……//否则,同理向下执行,交给文档、框架、及应用程序执行自身的OnCmdMsg。

}

到此,CFrameWnd:: OnCmdMsg完成了把WM_COMMAND消息传递到视对象、文档对象及应用程序对象实现消息响应。

写了这么多,我们清楚MFC消息映射与命令传递的大致过程。现在,我们来看MFC“神秘代码”,会发觉好看多了。

先看DECLARE_MESSAGE_MAP()宏,它在MFC中定义如下:

#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()定义了我们熟悉的两个结构和一个函数,显而易见,这个宏为每个需要实现消息映射的类提供了相关变量和函数。



现在集中精力来看一下BEGIN_MESSAGE_MAP,END_MESSAGE_MAP和ON_COMMAND三个宏,它们在MFC中定义如下(其中ON_COMMAND与另外两个宏并没有定义在同一个文件中,把它放到一起是为了好看):

#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 ON_COMMAND(id, memberFxn) \

{ WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSig_vv, (AFX_PMSG)&memberFxn },



#define END_MESSAGE_MAP() \

{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \

}; \



一下子看三个宏觉得有点复杂,但这仅仅是复杂,公式性的文字代换并不是很难。且看下面例子,假设我们框架中有一菜单项为“Test”,即定义了如下宏:

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)

ON_COMMAND(ID_TEST, OnTest)

END_MESSAGE_MAP()



那么宏展开之后得到如下代码:

const AFX_MSGMAP* CMainFrame::GetMessageMap() const

{ return &CMainFrame::messageMap; }

///以下填入消息表映射信息

const AFX_MSGMAP CMainFrame::messageMap =

{ &CFrameWnd::messageMap, &CMainFrame::_messageEntries[0] };

//下面填入保存着当前类感兴趣的消息,可填入多个AFX_MSGMAP_ENTRY对象

const AFX_MSGMAP_ENTRY CMainFrame::_messageEntries[] =

{

{ WM_COMMAND, CN_COMMAND, (WORD)ID_TEST, (WORD)ID_TEST, AfxSig_vv, (AFX_PMSG)&OnTest }, // 加入的ID_TEST消息参数

{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } //本类的消息映射的结束项

};



大家知道,要完成ID_TEST消息映射,还要定义和实现OnTest函数。在此即要在头文件写afx_msg void OnTest()并在源文件中实现它。根据以上所学的东西,我们知道了当ID为ID_TEST的命令消息发生,最终会执行到我们写的OnTest函数。



至此,MFC六大关键技术写完了。其中写得最难的是消息映射与命令传递,除了技术复杂之外,最难的是有许多避不开的代码。为了大家看得轻松一点,我把那繁杂的宏放在文章最后,希望能给你阅读带来方便。

其实,较早前这就描述过MFC六大关键技术,也在一些论坛上发表过,但后三篇不知如何遗失了。许多朋友向我索稿,但苦于没有备份,也不想枯燥地重写,故推延至今,请大家见谅。

还有,许多朋友说(二)(三)写得十分不明白,这也是我意料到的地方。我写文章,很少回头看一下,有时自己也不知道自己写了什么。有许多朋友把修改后的版本给我,虽然修改了一点点,甚至仅仅修改了一下错别字,但读起来容易理解很多。对此,我内心十分感激。如果有更多的时间,我会修改一下前面的文章,放到我的网站上去!



顺便说一下:我的QQ:14653353被盗了,迟些才打算要回它。

大家可到我公司的网站:www.fmpc.cn或我的个人网站:liyi.96.cn联系我!
《深入浅出MFC》分为四大篇。第一篇提出学习MFC程序设计之前的必要基础,包括Widnows程序的基本观念以及C++的高阶议题。“学前基础”是相当主观的认定,但作者是甚于自己的学习经验以及教学经验,其挑选应该颇具说服力。第二篇介绍Visual C++整合环境开发工具。此篇只是提纲挈领,并不企图取代Visual C++使用手册;然而对于软件使用的老手,此篇或已足以帮助掌握Visual C++整合环境。工具的使用虽然谈不上学问,但在视觉化软件开发过程中扮演极重角色。第三篇介绍application framework的观念,以及MFC骨干程序,所谓骨干程序,是指Visual C++的工具AppWizard所产生出来的程序码。当然,AppWizard会根据使用者的选项做出不同的程序码,作者据以解说的是大众化选项下的产品。第四篇以微软公司附于Visual C++光碟片上的一个范例程序Scribble为主轴,一步一步加上新的功能;并在其间深入介绍Runtime Type Information(RTTI)、Dynamic Creation、Persistence(Serialization)、Message Mapping、Command Routing等核心技术。这些技术正是其他专著最缺乏的部分。此篇的最后数章则脱离Scribble程序,另成一格。 目录: 第一篇 勿在浮砂筑高台 第一章 Win32 程序基本概念 第二章 C++的重要性质 第三章 MFC关键技术之仿真 第二篇 欲善工事先利其器 第四章 Visual C++ 集成开发环境 第三篇 浅出MFC程序设计 第章 总观Application Framework 第MFC程序的生死因果 第七章 简单而完整:MFC骨干程序 第四篇 深入MFC 程序设计 第八章 Document-View深入探讨 第九章 消息映射命令传递 第十章 MFC与对话框 第十一章 Vies功能的加强与重绘效率的提高 第十二章 打印与预览 第十三章 多重文件与多重视图 第十四章 MFC 多线程程序设计 第十章 站在众人的肩膀――使用Components & ActiveX Controls 第篇 附录 ——《豆瓣读书》
深入浅出MFC(第二版) 目录 第0章 你一定要知道(导读) 这本书适合谁 你需要什么技术基础 你需要什么软硬件环境 让我们使用同一种语言 本书符号习惯 本书例程的取得 范例程序说明 与前版本之差异 如何联络作者 第一篇 勿在浮砂筑高台 第1章 Win32程序基本概念 Win32程序开发流程 需要什么函数库(.LIB) 需要什么头文件(.H) 以消息为基础,以事件驱动之(message based,event driven) 一个具体而微的Win32程序 程序进入点WinMain 窗口类之注册与窗口之诞生 消息循环 窗口的生命中枢:窗口函数 消息映射(Message Map)的雏形 对话框的运行 模块定义文件(.DEF) 资源描述档(.RC) Widnows程序的生与死 空闲时间的处理:OnIdle Console程序 Console程序与DOS程序的差别 Console程序的编译链接 JBACKUP:Win32 Console程序设计 MFCCON:MFC Console程序设计 行程与线程(Process and Thread) 核心对象 一个行程的诞生与死亡 产生子行程 一个线程的诞生与死亡 以_beginthreadex取代CreateThread 线程优先级(Priority) 多线程程序设计实例 第2章 C++的重要性质 类及其成员——谈封装(encapsulation) 基类与派生类:谈继承(Inheritance) this指针 虚拟函数与多态(Polymorphism) 类与对象大解剖 Object slicing与虚拟函数 静态成员(变量与函数) C++程序的生与死:兼谈构造函数与解构函数 四种不同的对象生存方式(in stack、in heap、global、local static) 执行期类型信息(RTTI) 动态生成(Dynamic Creation) 异常处理(Exception Handling) Template Template Functions Template Classes Template的编译与链接 第3章 MFC关键技术之仿真 MFC类层次结构 Frame 1范例程序 MFC程序的初始化过程 Frame 2范例程序 RTTI(执行期类型识别) 类别型录网与CRuntimeClass DECLARE_DYNAMIC/IMPLEMENT_DYNAMIC宏 Frame 3范例程序 IsKindOf(类型识别) Frame 4范例程序 Dynamic Creation(动态生成) DECLARE_DYNCREATE/IMPLEMENT_DYNCREATE宏 Frame 6范例程序 Persistence(永久保存)机制 Serialize(数据读写) DECLARE_SERIAL/IMPLEMENT_SERIAL宏 没有范例程序 Message Mapping(消息映射) Frame 7范例程序 Command Routing(命令传递) Frame 8范例程序 本章回顾 第二篇 欲善工事先利其器 第4章 Visual C++集成开发环境 安装与组成 四个重要的工具 内务府总管:Visual C++集成开发环境 关于project 关于工具设定 Source Browser Online Help 调试工具 VC++调试器 Exception Handling 程序代码产生器:AppWizard 东圈西点完成MFC程序骨干 威力强大的资源编辑器 Icon编辑器 Cursor编辑器 Bitmap编辑器 工具栏(Toolbar)编辑器 VERSIONINFO资源编辑器 字符串表格(Accelerator)编辑器 菜单(Menu)编辑器 加速键(Accelerator)编辑器 对话框(Dialog)编辑器 Console程序的项目管理 第三篇 浅出MFC程序设计 第5章 总观Application Framework 什么是Application Framework? 侯捷怎么说 我怎么说 别人怎么说 为什么使用Application Framework Microsoft Foundation Classes(MFC) 白头宫女话天宝:Visual C++与MFC 纵览MFC General Purpose classes CObject 数据处理类(collection classes) 杂项类 异常处理类(exception handling classes) Windows API classes Application framework classes High level Abstractions Afx全局函数 MFC宏(macros) MFC数据类型(data types) 第6章 MFC程序的生死因果 不二法门:熟记MFC类的层次结构 需要什么函数库? 需要什么头文件? 简化的MFC程序结构——以Hello MFC为例 Hello程序程序代码 MFC程序的来龙去脉(causal relations) 我只借用两个类:CWinApp和CFrameWnd CWinApp——取代WinMain的地位 CFrameWnd——取代WndProc的地位 引爆器——Application object 隐晦不明的WinMain AfxWinInit——AFX内部初始化操作 CWinApp::InitApplication CMyWinApp::InitInstance CFrameWnd::Create产生主窗口(并先注册窗口类) 奇怪的窗口类名称Afx:b:14ae:6:3e8f 窗口显示与更新 CWinApp::Run——程序生命的活水源头 把消息与处理函数连接在一起:Message Map机制 来龙去脉总整理 Callback函数 空闲时间(idle time)的处理:OnIdle Dialog与Control 通用对话框(Common Dialogs) 本章回顾 第7章 简单而完整:MFC骨干程序 不二法门:熟记MFC类层次结构 MFC程序的UI新风貌 Document/View支撑你的应用程序 利用Visual C++工具完成Scribble step0 骨干程序使用哪些MFC类? Document Template的意义 Scribble的Document/View设计 主窗口的诞生 工具栏和状态栏的诞生(Toolbar&Status bar) 鼠标拖放(Drag and Drop) 消息映射(Message Map) 标准菜单File/Edit/View/Window/Help 对话框 改用CEditView 第四篇 深入MFC程序设计 第8章 Document-View深入探讨 为什么需要Document-View(形而上) Document View Document Frame(View Frame) Document Template CDocTemplate管理CDocument/CView/CFrameWnd Scribble Step1的Document——数据结构设计 MFC Collection Classes的选用 CScribbleDoc的修改 文件:一连串的线条 线条与坐标点 Scribble Step 1的View:数据重绘与编辑 CScribbleView的修改 View的重绘操作:GetDocument和OnDraw ClassWizard的辅佐 WizardBar的辅佐 Serialize:对象的档案读写 Serialization以外的档案读写操作 台面上的Serialize操作 台面下的Serialize写档奥秘 台面下的Serialize读档奥秘 DYNAMIC/DYNCREATE/SERIAL三宏 Serializable的必要条件 CObject类 IsKindOf IsSerializable CObject::Serialize CArchive类 operator《和operator》 效率考虑 自定SERIAL宏给抽象类使用 在CObList中加入CStroke以外的类 Document与View交流——为Step4做准备 第9章 消息映射命令传递 到底要解决什么 消息分类 万流归宗Command Target(CCmdTarget) 三个奇怪的宏,一张巨大的网 DECLARE_MESSAGE_MAP宏 消息映射网的形成:BEGIN…/ON…/END…宏 米诺托斯(Minotauros)与西修斯(Theseus) 二万千里长征——消息传递 直线上溯(一般Windows消息) 拐弯上溯(WM_COMMAND命令消息) 罗塞达碑石:AfxSig_xx的奥秘 Scribble Step2:UI对象的变化 改变菜单 改变工具栏 利用ClassWizard连接命令项识别码与命令处理函数 维护UI对象状态(UPDATE_COMMAND_UI) 本章回顾 第10章 MFC与对话盒 对话框编辑器 利用ClassWizard连接对话框与其专用类 对话框的消息处理函数 对话框数据交换与校验(DDX&DDV) 如何唤起对话框 本章回顾 第11章 View功能的加强与重绘效率的提高 同时修改多个Views:UpdateAllViews和OnUpdate 在View中定义一个hint 把hint传给OnUpdate 利用hint增加重绘效率 可卷动的窗口:CScrollView 大窗口中的小窗口:Splitter 切分窗口的功能 切分窗口的程序概念 切分窗口的实现 本章回顾 第12章 打印与预览 概述 打印操作的后台原理 MFC默认的打印机制 Scribble打印机制的增强 打印机的页和文件的页 配置GDI绘图工具 尺寸与方向:关于映射方式(坐标系统) 分页 页眉与页脚 动态计算页码 打印预览(Print Preview) 本章回顾 第13章 多重文件与多重显示 MDI和SDI 多重显示(Multiple Views) 窗口的动态切分 窗口的静态切分 CreateStatic和CreateView 窗口的静态三叉切分 Graph范例程序 静态切分窗口之观念整理 同源子窗口 CMDIFrameWnd::OnWindowNew Text范例程序 非标准做法的缺点 多重文件 新的Cocument类 新的Document Template 新的UI系统 新文件的档案读写操作 第14章 MFC多线程程序设计 从操作系统层面看线程 三个观念:模块、行程和线程 线程优先级(Priority) 线程调度(Scheduling) Thread Context 从程序设计层面看线程 Worker Threads和UI Threads 错误观念 正确态度 MFC多线程程序设计 探索CWinThread 产生一个Worker Thread 产生一个UI Thread 线程的结束 线程与同步控制 MFC多线程程序例程 第15章 定制一个AppWizard 到底Wizard是什么? Custom AppWizard的基本操作 剖析AppWizard Components Dialog Templates和Dialog classes Macros Directives 动手修改Top Studio AppWizard 利用资源编辑器修改IDD_CUSTOM1对话框画面 利用ClassWizard修改IDD_CUSTOM1对话框的对应类CCustomlDlg 改写OnDismiss虚拟函数,在其中定义macros 修改text template Top Studio AppWizard执行结果 更多的信息 第16章 站上众人的肩膀——使用Components&activeX Controls 什么是Component Gallery 使用Components Splash screen system Info for About Dlg Tip of the Day Components实际运用:ComTest程序 修改ComTest程序内容 使用ActiveX Controls ActiveX Control基础观念:Properties、Methods、Events ActiveX Controls的大使用步骤 使用ActiveX Control:OcxTest程序 第篇 附录 附录A 无责任书评:从摇篮到坟墓Windows的完全学习 无责任书评:MFC四大天王 附录B Scribble Step 5完整原始码 附录C Visual C++5.0MFC范例程序一览 附录D 以MFC重建DBWIN

16,551

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Creator Browser
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧