六、MFC 程序的生死因果 (学习笔记)

MFC程序的生死因果


项目HELLO


理想如果不向实际做点妥协,理想就会归于尘土。

中华民国还得十次革命才得建立,对象导向怎能把一切传统都抛开。


以传统的C/SDK 撰写Windows 程序,最大的好处是可以清楚看见整个程序的来龙去脉和消息动向,然而这些重要的动线在MFC 应用程序中却隐晦不明,因为它们被Application Framework包起来了。这一章主要目的除了解释MFC 应用程序的长像,也要从MFC 源代码中检验出一个Windows 程序原本该有的程序进入点(WinMain)、视窗类别注册(RegisterClass)、窗口产生(CreateWindow )、消息循环(Message Loop )、窗口函数(Window Procedure)等等动作,抽丝剥茧彻底了解一个MFC 程序的诞生与结束,以及生命过程。


为什么要安排这一章?了解MFC 内部构造是必要的吗?看电视需要知道映射管的原理吗?开汽车需要知道传动轴与变速箱的原理吗?学习MFC 不就是要一举超越烦琐的Windows API?啊,厂商(不管是哪一家)广告给我们的印象就是,藉由可视化的工具我们可以一步登天,基本上这个论点正确,只是有个但是:你得学会操控Application Framework 。


想象你拥有一部保时捷,风驰电挚风光得很,但是引擎盖打开来全傻了眼。如果你懂汽车内部运作原理,那么至少开车时「脚不要老是含着离合器,以免来令片磨损」这个道理背后的原理你就懂了,「踩煞车时绝不可以同时踩离合器,以免失去引擎煞车力」这个道理背后的原理你也懂了,甚至你的保时捷要保养维修时或也可以不假外力自己来。


不要把自己想象成这场游戏中的后座车主,事实上作为这本技术书籍的读者的你,应该是车厂师傅。







我希望你了解,本书之所以在各个主题中不厌其烦地挖MFC内部动作,解释骨干程序的每一条指令,每一个环节,是为了让你踏实地接受MFC,进而有能力役使MFC。你以为这是一条远路?呵呵,似远实近!



不二法门:熟记MFC 类别的阶层架构


MFC 在1.0版时期的诉求是「一组将SDK API 包装得更好用的类别库」,从2.0版开始更进一步诉求是一个「Application Framework」,拥有重要的Document-View架构;随后又在更新版本上增加了OLE 架构、DAO 架构... 。为了让你有一个最轻松的起点,我把第一个程序简化到最小程度,舍弃Document-View  架构,使你能够尽快掌握C++/MFC 程序的面貌。这个程序并不以AppWizard 制作出来,也不以ClassWizard 管理维护,而是纯手工打造。毕竟Wizards 做出来的程序代码有一大堆批注,某些批注对Wizards 有特殊意义,不能随便删除,却可能会混淆初学者的视听焦点;而且Wizards 所产生的程序骨干已具备Document-View  架构,又有许多奇奇怪怪的宏,初学者暂避为妙。我们目前最想知道的是一个最阳春的MFC 程序以什么面貌呈现,以及它如何开始运作,如何结束生命。


SDK 程序设计的第一要务是了解最重要的数个API  函数的意义和用法,像是RegisterClass、CreateWindow 、GetMessage 、DispatchMessage,以及消息的获得与分配。MFC 程序设计的第一要务则是熟记MFC 的类别阶层架构,并清楚知晓其中几个一定会用到的类别。本书最后面有一张MFC 4.2 架构图,叠床架屋,令人畏惧,我将挑出单单两个类别,组合成一个"Hello MFC"  程序。这两个类别在MFC的地位如图6-1所示。



需要什么函数库?


开始写码之前,我们得先了解程序代码以外的外围环境。第一个必须知道的是,MFC 程序需要什么函数库?SDK 程序联结时期所需的函数库已在第一章显示,MFC 程序一样需要它们:





此外,应用程序还需要联结一个所谓的MFC 函数库,或称为AFX 函数库,它也就是MFC这个application framework  的本体。你可以静态联结之,也可以动态联结之,AppWizard给你选择权。本例使用动态联结方式,所以需要一个对应的MFC import 函数库:




我们如何在联结器(link.exe )中设定选项,把这些函数库都联结起来?稍后在HELLO.MAK中可以一窥全貌。


如果在Visual C++  整合环境中工作,这些设定不劳你自己动手,整合环境会根据我们圈选的项目自动做出一个合适的makefile。这些makefile 的内容看起来非常诘屈聱牙,事实上我们也不必太在意它,因为那是整合环境的工作。这一章我不打算依赖任何开发工具,一切自己来,你会在稍后看到一个简洁清爽的makefile。



需要什么头文件?


SDK 程序只要包含WINDOWS.H 就好,所有API  的函数声明、消息定义、常数定义、宏定义、都在WINDOWS.H 档中。除非程序另调用了操作系统提供的新模块(如CommDlg、ToolHelp 、DDEML...),才需要再各别包含对应的.H 档。




MFC 程序不这么单纯,下面是它常常需要面对的另外一些.H 档:


STDAFX.H -  这个文件用来做为Precompiled header file(请看稍后的方块说明),其内只是包含其它的MFC 头文件。应用程序通常会准备自己的STDAFX.H ,例如本章的Hello 程序就在STDAFX.H 中包含AFXWIN.H。


AFXWIN.H -  每一个Windows MFC 程序都必须包含它,因为它以及它所包含的文件声明了所有的MFC 类别。此档内含AFX.H,后者又包含AFXVER_.H,后者又包含AFXV_W32.H,后者又包含WINDOWS.H(啊呼,终于现身)。


AFXEXT.H -  凡使用工具栏、状态列之程序必须包含这个文件。


AFXDLGS.H - 凡使用通用型对话框(Common Dialog)之MFC 程序需包含此档,其内部包含COMMDLG.H。


AFXCMN.H - 凡使用Windows 95 新增之通用型控制组件(Common Control)之MFC 程序需包含此文件。


AFXCOLL.H - 凡使用Collections Classes (用以处理数据结构如数组、串行)之程序必须包含此文件。


AFXDLLX.H - 凡MFC extension DLLs 均需包含此档。

AFXRES.H - MFC 程序的RC文件必须包含此档。MFC 对于标准资源(例如File、Edit  等菜单项目)的ID 都有默认值,定义于此文件中,例如:

 

// File commands
#define ID_FILE_NEW         0xE100
#define ID_FILE_OPEN        0xE101
#define ID_FILE_CLOSE       0xE102
#define ID_FILE_SAVE        0xE103
#define ID_FILE_SAVE_AS     0xE104
...
// Edit commands
#define ID_EDIT_COPY        0xE122
#define ID_EDIT_CUT         0xE123
...

这些菜单项目都有预设的说明文字(将出现在状态列中),但说明文字并不会事先定义于此文件,AppWizard 为我们制作骨干程序时才把说明文字加到应用程序的RC文件中。第4章的骨干程序Scribble step0  的RC 档中就有这样的字符串表格:

STRINGTABLE DISCARDABLE
BEGIN
  ID_FILE_NEW      "Create a new document"
  ID_FILE_OPEN     "Open an existing document"
  ID_FILE_CLOSE    "Close the active document"
  ID_FILE_SAVE     "Save the active document"
  ID_FILE_SAVE_AS  "Save the active document with a new name"
  ...
  ID_EDIT_COPY "Copy the selection and puts it on the Clipboard"
  ID_EDIT_CUT  "Cut the selection and puts it on the Clipboard"
  ...
END


所有MFC 头文件均置于\MSVC\MFC\INCLUDE  中。这些文件连同Windows SDK 的包含档WINDOWS.H、COMMDLG.H、TOOLHELP.H、DDEML.H... 每每在编译过程中耗费大量的时间,因此你绝对有必要设定Precompiled header 。



简化的MFC 程序架构-以Hello MFC 为例


现在我们正式进入MFC 程序设计。由于Document/View架构复杂,不适合初学者,所以我先把它略去。这里所提的程序观念是一般的MFC Application Framework 的子集合。本章程序名为Hello,执行时会在窗口中从天而降"Hello, MFC"   字样。Hello 是一个非常简单而具代表性的程序,它的代表性在于:

■   每一个MFC 程序都想从MFC 中衍生出适当的类别来用(不然又何必以MFC 写程序呢),其中两个不可或缺的类别CWinApp 和CFrameWnd 在Hello程序中会表现出来,它们的意义如图6-2。


■   MFC 类别中某些函数一定得被应用程序改写(例如CWinApp :: InitInstance),这在Hello 程序中也看得到。


■   菜单和对话框,Hello 也都具备。
 

图6-3 是Hello 源文件的组成。第一次接触MFC 程序,我们常常因为不熟悉MFC 的类别分类、类别命名规则,以至于不能在脑中形成具体印象,于是细部讨论时各种信息及说明彷如过眼云烟。相信我,你必须多看几次,并且用心熟记MFC 命名规则。


图6-3  之后是Hello 程序的源代码。由于MFC 已经把Windows API 都包装起来了,源代码再也不能够「说明一切」。你会发现MFC 程序很有点见林不见树的味道:

■看不到WinMain,因此不知程序从哪里开始执行。


■看不到RegisterClass 和CreateWindow ,那么窗口是如何做出来的呢?


■看不到Message Loop (GetMessage /DispatchMessage ),那么程序如何推动?


■看不到Window Procedure,那么窗口如何运作?


我的目的就在铲除这些困惑。


Hello  程序源代码


■   HELLO.MAK - makefile


■   RESOURCE.H -   所有资源ID 都在这里定义。本例只定义一个IDM_ABOUT。


■   JJHOUR.ICO -  图标文件,用于主窗口和对话框。


■   HELLO.RC - 资源描述档。本例有一份菜单、一个图标、和一个对话框。


■   STDAFX.H -   包含AFXWIN.H。


■   STDAFX.CPP -  包含STDAFX.H ,为的是制造出Precompiled header 。


■   HELLO.H -   声明CMyWinApp 和CMyFrameWn d。


■   HELLO.CPP -  定义CMyWinApp 和CMyFrameWn d。


注意:没有模块定义文件.DEF?是的,如果你不指定模块定义文件,联结器就使用默认值。


  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值