友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
富士康小说网 返回本书目录 加入书签 我的书架 我的书签 TXT全本下载 『收藏到我的浏览器』

C语言实例教程(PDF格式)-第16部分

快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!





// 唯一的 CDialogDemoApp 对象  



CDialogDemoApp theApp;  



/////////////////////////////////////////////////////////////////////////////  



// CDialogDemoApp 初始化  



BOOL CDialogDemoApp::InitInstance()  



{  



AfxEnableControlContainer();  



// 标准初始化  



// 如果你不需要使用这些特性,并且希望减小最终可执行文件的大小,你可以删除  



// 下面的特定的初始化过程中不需要的部分。  



#ifdef _AFXDLL  



Enable3dControls(); // 当通过共享 DLL 使用 MFC 时调用  



#else  



Enable3dControlsStatic(); // 当通过静态链接到 MFC 时调用  



#endif  


…………………………………………………………Page 176……………………………………………………………

CDialogDemoDlg dlg;  



m_pMainWnd = &dlg;  



int nResponse = dlg。DoModal();  



if (nResponse == IDOK)  



{  



// TODO: 在这里添加当使用 OK 关闭对话框时的处理代码  



}  



else if (nResponse == IDCANCEL)  



{  



// TODO: 在这里添加当使用 Cancel 关闭对话框时的处理代码  



}  



// 由于对话框已被关闭,返回 FALSE 并退出应用程序,而不需要启动应用程序  



// 消息泵。  



return FALSE;  



}  



上面的源代码为类CDialogDemoApp提供了一个空的构造函数和一个对 

InitInstance的默认重载。我们把讨论的重点放在InitInstance成员 

函数上。在InitInstance成员函数的一开始,先调用MFC全局函数 

AfxEnableControlContainer,该函数为应用程序提供了对OLE控件 

(新的术语称作ActiveX控件)的支持。  



接着,InitInstance成员函数调用类CWinApp的成员函数 

Enable3dControls或Enable3dControlsStatic以允许对话框和窗口可 

以使用具有三维外观的控件。这两个成员函数加载CTL3D32。DLL并且 

注册应用程序。Enable3dControls和Enable3dControlsStatic的区别 

在于一个在链接到MFC动态链接库时使用,而另一个在使用MFC的静态 

链接时使用。  



MFC 自动为以下窗口类提供3D控件效果:  



   l CDialog  



   l CDialogBar  


…………………………………………………………Page 177……………………………………………………………

  l CFormView  



  l CPropertyPage  



  l CPropertySheet  



  l CControlBar  



  l CToolBar  



如果你所希望得到3D效果的控件属于以上类型之一的话,你只需调用 

Enable3dControls或Enable3dControlsStatic即可。反之则必须直接 

调用相应的CTL3D32 API函数。  



然后在InitInstance中定义了类型为CDialogDemoDlg的对象dlg,然 

后将其指针赋予类型为CWnd的成员变量m_pMainWnd。成员变量 

m_pMainWnd用来保存指向线程主窗口对象的指针,当由m_pMainWnd引 

用的窗口被关闭时,该线程由MFC 自动终止。当应用程序的主线程被 

终止时,该应用程序相应的也被终止。如果该成员的值为NULL,则应 

用程序的CWinApp对象的主窗口被用来判断线程何时终止。成员 

m_pMainWnd具有共有访问权限。对于工作者线程而言,该数据成员的 

值从其父线程继承。  



接着InitInstance调用了对象dlg的成员函数DoModal,该成员函数以 

模态方式调用对话框并在结束时返回对话框的结果。在对话框激活 

时,该成员函数处理所有与用户的交互,也就是说,对于模态对话 

框,用户不可以在对话框关闭之前与其它窗口进行交互。  



如果用户单击了对话框中的OK或Cancel按钮,相应的消息处理函数, 

如OnOK或OnCancel被调用以试图关闭对话框。OnOK成员函数的默认行 

为为验证和更新对话框数据并以结果IDOK关闭对话框,OnCancel的默 

认行为为以结果IDCANCEL关闭对话框并不更机关报所有对话框数据。 

通过重载这些消息处理函数可以改变它们的行为。  



在DoModal返回时,对话框将被关闭,理所当然的,基于该对话框的 

应用程序也应该被关闭,因此在InitInstance的最后使用了语句  



return FALSE;  



            第三节 MFC应用程序的消息循环  



上面的对应用程序的类的定义和声明还包括了MFC的消息循环 


…………………………………………………………Page 178……………………………………………………………

(message   loop),下面我们来详细的描述MFC应用程序的消息循环。 

框架应用程序处理Windows消息的方式同其它Windows应用程序是类似 

的,只不过它提供了一些方法来使得这个过程更加的方便,更加的易 

于维护和更好的包装。  



  l 注意:  



  l 为便于读者理解,在此我们给出一个MFC对对话框控制的支持的树 

   图(如图4。8所示)。  



在类CWinApp的Run成员函数中的消息循环获取各种事件所产生的排队 

消息(queued  message),应用程序消息循环的框架实现是将它们分发 

到合适的窗口。  



在MFC中,每一个单独的消息都由一个专门的函数进行处理,这种称 

作消息处理函数 (message…handler  function或message  handler或 

handler)的专门函数以类的成员函数的方式进行定义。处理命令消息 

的函数还常被称作命令处理函数 (mand handler)。  



Windows应用程序是消息驱动的,因此编写消息处理函数就成了编写 

框架应用程序的工作中的一个很大的组成部分。  



                               



             图4。 8 MFC 中对话框控件类的树状结构图  



每一个有能力获取消息或命令的框架类都有它自己的消息映射 

(message   map),框架使用消息映射来建立消息和命令到它们的处理 

函数之间的链接。所有从类CCmdTarget派生的类都可以有它自己的消 

息映射。尽管我们常常区别消息和命令两个术语,但这里所说的消息 


…………………………………………………………Page 179……………………………………………………………

映射同时对它们进行处理。  



下面我们来看一下消息是如何发送和获取的。  



很多消息来自于用户和程序之间的交互:当用户使用鼠标单击了菜单 

项或工具条按钮或按下了快捷键时,即产生了命令。同样由用户产生 

的Windows消息还可以来 自移动窗口或改变窗口的大小。此外,当程 

序启动或终止、窗口获得或失去焦点等等事件发生时,相应的 

Windows消息也将被发送。控件作为一种特殊形式的窗口,相应的控 

件通知消息也在类似的情形下产生。  



Run成员函数用来获取消息并将它们发送到合适的窗口,很多的命令 

消息被发送到应用程序的主窗口,由类库预先定义的WindowProc函数 

获得这些消息,然后根据所获得的消息的类型来以不同的方式对它们 

进行处理。  



最初接受消息的必须是一个窗口对象。Windows消息通常直接由该窗 

口对象进行处理。而命令消息一般由应用程序的主框架窗口开始,按 

照命令路径描述的命令目标链进行处理。  



当一个命令目标链获得消息或命令时,它将搜索它的消息映射以寻找 

匹配项。如果该消息的一个处理函数存在,该处理函数将被调用。  



与命令不同,对于标准Windows消息,它们并不经过命令目标链,通 

常由该消息的目标窗口进行处理,这个 目标窗口可能是主框架窗口, 

也可能是一个MDI子窗口、一个标准控件、对话框、视或其它形式的 

子窗口。  



在运行时,每一个Windows窗口都与一个窗口对象建立关联,该窗口 

对象由直接的或间接的由类CWnd派生,并且有着它自己的消息映射和 

处理函数。框架使用这个消息映射来将到来的消息映射到它们的处理 

函数。  



对于命令,我们所需做的只是建立命令到它们的处理函数之间的链 

接,通常使用ClassWizard来完成这一步工作,然后编写绝大多数的 

命令处理程序。  



Windows消息通常发送到主框架窗口,但是命令消息将可能传送到其 

它的对象,框架通过一个命令目标对象的标准顺序来传递命令,这些 

命令目标对象至少有一个可能 (并不是一定)包含该命令的处理函数。 

每一个命令目标对象检查它的消息映射以查看是否有一个命令处理函 

数来处理到来的消息。  


…………………………………………………………Page 180……………………………………………………………

不同的命令目标类在不同的时候检查它们 自己的消息映射。典型的, 

一个类将命令传送给其它一些类,这使得这些类具有最先处理该消息 

的机会。如果这些类中没有一个处理了该命令,最初的那个类将检查 

它的消息映射,然后,如果它也没有提供相应的处理函数,它可能会 

将该命令传送给更多的命令目标。表4。2描述了构成这一顺序的结 

构。通常的顺序是先传送给当前激活子命令目标对象,再传送给自 

身,最后传送给其它的命令目标。  



                    表4。 2 标准命令传送路径  



              该类型的对象获     它将按下面的顺序给自 

              取某一命令       身和其它命令目标对象 

              时……         处理该命令的机会  



              MDI框架窗口     1。       当前激活 

               (CMDIFrameWnd)   CMDIChildWnd  

                          2。 当前框架窗口自身  

                          3。  应用程序 (CWinApp对 

                          象)  



              文档框架窗口      1。 当前激活视  

               (CframeWnd, 2。 当前框架窗口  

              CMDIChildWnd)  3。  应用程序 (CWinApp对 

                          象)  



              视           1。 当前视  

                          2。 与视相关联的文档  



              文档          1。 当前文档  

                          2。  与文档相关联的文档 

                          模板  



              对话框         1。 当前对话框  

                          2。  拥有当前对话框的窗 

                          口  

                          3。  应用程序 (CWinApp对 

                          象)  



上面的过程看起来很复杂,并且添加了程序的开销,但是相比处理程 

序对命令的响应来说,传送命令的开销要小得多,因为仅当用户与一 

个用户界面对象进行交互时框架才生成相应的命令。  



当使用AppWizard创建新的框架应用程序 (skeleton            application) 

时,AppWizard就已经为它所创建的每一个命令目标类编写了相应的 

消息映射。在这些消息映射中,有一些已经添加了对某些消息和预定 

义命令的处理,而其它一些只是为了下一步添加处理函数的占位符。  


…………………………………………………………Page 181……………………………………………………………

类的消息映射位于该类的。CPP文件中,我们通常使用ClassWizard为 

每一个类将要处理的消息和命令添加入口。一个典型的消息映射具有 

如下的结构,它来自文件DialogDemo。cpp :  



BEGIN_MESSAGE_MAP(CDialogDemoApp; CWinApp)  



//{{AFX_MSG_MAP(CDialogDemoApp)  



// 注意 ClassWizard 将在此添加或删除映射宏。  



// 不要删除你在这里看到的这些生成代码块 !  



//}}AFX_MSG  



ON_MAND(ID_HELP; CWinApp::OnHelp)  



END_MESSAGE_MAP()  



上面的消息映射包括一系列的宏。消息映射位于两个宏—— 

BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间,其它的宏,如 

ON_MAND构成了消息映射的内容。  



  l 注意:  



  l 在消息映射宏的后面没有分号。  



消息映射还包括了下面形式的注释:  



//{{AFX_MSG_MAP(CDialogDemoApp)  



//}}AFX_MSG_MAP  



在两行注释之间包括了消息映射入口,但不要求所有的消息映射入口 

都在这两行注释之间。当使用ClassWizard编写入口时,它将使用这 

些特殊的注释。所有由ClassWizard生成的注释都位于这两行注释之 

间。  



  l 注意:  



  l 除非你确实不想在程序中再使用ClassWizard,不要更改// 

    {{AFX_MSG_MAP和//}}AFX_MSG_MAP记号,这是ClassWizard进行程 

    序相关数据库管理的特殊标记。  



当使用ClassWizard创建新类时,相应的消息映射将由ClassWizard 自 

动生成。而且,在前面的示例代码中,我们还看到了不要随意修改由 

ClassWizard生成的消息映射项的警告。但是,对于那些有经验的程 


…………………………………………………………Page 182……………………………………………………………

序员,使用源代码编辑器来手动的创建消息映射也是完全可行的。  



我们注意到前面的BEGIN_MESSAGE_MAP具有下面的格式,它具有两个 

参数:CDialogDemoApp和CWinApp。  



BEGIN_MESSAGE_MAP(CDialogDemoApp; CWinApp)  



第一个参数CDialogDemoApp表示消息映射所属的类,第二个参数 

CWinApp表示CDialogDemoApp的直接基类,这向我们暗示了一点,这 

就是说,如果框架在类CDialogDemoApp中没有找到某一特定消息或命 

令的映射入口,它将按照类的继承结构依次查找该与该消息或命令相 

匹配的入口。如果按照这种方式还是未能找到一个匹配的映射项,对 

于命令,框架将将它传送给下一个命令目标,对于标准Windows消 

息,框架将将它传递给一个合适的默认窗口过程。为了加速消息映射 

匹配的速度,框架使用了一种类似于磁盘缓存的机制,它保存了与最 

近匹配项有关的信息,以便在获取相同的消息时可以很快的找到与消 

息相匹配的消息映射。事实上,消息映射同使用虚函数相比,在某些 

方面要更为有效。  



下面我们讨论一下消息处理函数的声明。消息处理函数的声明需要遵 

从一些规则与协议,这些规则和协议因消息所属的种类不同而有所不 

同。  



在类CWnd中定义了标准的Windows消息处理函数,这些消息以前缀WM_ 

开头。相应的消息处理函数的命名基于消息的名称。举例来说,消息 

WM_PAINT的处理函数在CWnd中被声明为  



afx_msg void OnPaint();  



关键字afx_msg使得上面的函数看起来和其它的CWnd成员函数有所不 

同,然而实际上,经过预处理之后,afx_msg将被空白所代替,也就 

是说,除了可以使程序员很清楚的的知道哪一些函数是消息处理函 

数,而哪一些是一般的成员函数。(实际上,该关键字为Microsoft 

公司为今后所作的保留字。)消息处理函数只通过消息映射来实现, 

而消息映射仅依赖于几个标准的预处理宏。  



如果需要重载在基类中定义的消息处理函数,只需简单的使用 

ClassWizard在派生类中定义一个具有相同原型的函数,并且为它添 

加消息映射入口。关于如果使用ClassWizard重载消息处理函数的示 

例我们将会在本书后面的内容中遇到,这里就不再赘述。  



在一些情况下,重载以后的处理函数应该在适当的地方调用基类的被 


…………………………………………………………Page 183……………………………………………………………

重载函数以使得基类和Windows可以处理这些消息。而在什么地方调 

用基类的被重载函数依环境而定。有时候我们需要根据一些条件来决 

定是否需要调用基类的被重载函数,而在另外的一些场合可能恰恰相 

反,我们需要基类的处理函数的返回值来决定是否或如何执行自己的 

处理函数代码。  



  l 注意:  



  l 有些时候我们可能会想到在将传递给消息处理函数的参数再传递 

   给基类的处理函数时修改它们。比如说,我们有可能想到通过修 

   改OnChar处理函数的nChar参数来在用户输入时屏蔽掉一些字符。 

   但是这样做是不安全的,如果一定需要这样做,我们应该使用类 

   CWnd的成员函数SendMessage,而不是直接修改传递给消息处理函 

   数的参数。  



按照惯例,我们在消息处理函数名的前面都使用了前缀 “On”。并且 

消息处理函数可能带有几个参数,也可能一个参数也没有。一些消息 

处理函数可以返回值,而另一些可能被声明为void。可以在Class  

Library  Reference中找到以WM_开头的消息的默认处理函数,它们都 

是类CWnd的成员函数,并且具有前缀 “On”。这些成员函数在类CWnd 

中的声明均带有前缀afx_msg。  



对于命令或控件通知消息,MFC并未提供默认的处理函数。因此,我 

们需要根据命名约定来自己命名或编写这些消息处理函数。当将命令 

或控件通知映射到处理函数时,ClassWizard根据命令ID或控件通知 

代码建议处理函数名。  



举个例子来说,按照命名约定,响应File菜单下的Open命令的消息处 

理函数将被命名为  



afx_msg void OnFileOpen();  



对于一些很常见的用户界面元素,在框架中已为它们预定义了一些命 

令ID,比如与File菜单下的Open命令对应的命令ID为ID_FILE_OPEN, 

这些预定义ID可以在文件AFXRES。H 中找到。下面是所支持的最重要的 

一些命令的列表:  



  l File菜单命令:New、Open、Close、Save、Save As、Page  

   Setup、Print Setup、Print、Print Preview、Exit以及最近使 

   用的文件  



  l Edit菜单命令:Clear、Clear All、Copy、Cut、Find、Paste、 


…………………………………………………………Page 184……………………………………………………………

   Repeat、Replace、Select All、Undo以及 Redo。  



  l View菜单命令:Toolbar以及Status Bar。  



  l Window菜单命令:New、Arrange、Cascade、Tile Horizontal、 

   Tile Vertical以及Split。  



  l Help菜单命令:Index、Using Help以及About。  



  l OLE命令 (位于Edit菜单):Insert New Object、Edit Links、 

   Paste Link、Paste Special以及typename Object (谓词命令)。  



再举一个例子,按照命名约定的建议,对一个标签为Default的按钮 

控件的BN_CLICKED通知消息处理函数将被命名为:  



afx_msg void OnClickedDefault();  



这样,我们就可以将IDC_DEFAULT的ID与一个命令相关联,这样该命 

令等价于应用程序指定的用户界面对象。  



以上讲到的两类消息 (命令和控件通知消息)的处理函数都不带任何参 

数,同时也不返回任何值。  



在上面的例子中,每一个处理函数都对应了单个命令ID或控件ID。然 

而,在MFC的消息映射机制中,我们还可能将单个处理函数对应一个 

命令ID或控件ID范围,但是,ClassWizard不支持命令ID或控件ID范 

围的处理函数的映射,因此我们必须手动的添加消息映射入口。由于 

到目前为止所讲述的内容还不足以提供了一个完整的示例,因此我们 

将在本书后面的内容中给出以上内容的示例程序。  



在本节的最后解释一下宏DECLARE_MESSAGE_MAP。该宏一般出现的支 

持消息映射的类定义的尾部。前面已经说过,每一个从CCmdTarget派 

生的类都可以提供了一个消息映射来处理消息,这时我们需要在类声 

明中使用宏DECLARE_MESSAGE_MA
返回目录 上一页 下一页 回到顶部 9 9
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!