﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-万水千山只等闲-随笔分类-MFC</title><link>http://www.cppblog.com/weining45/category/19453.html</link><description /><language>zh-cn</language><lastBuildDate>Tue, 05 Jun 2012 01:25:35 GMT</lastBuildDate><pubDate>Tue, 05 Jun 2012 01:25:35 GMT</pubDate><ttl>60</ttl><item><title>入门2</title><link>http://www.cppblog.com/weining45/archive/2012/06/05/177596.html</link><dc:creator>Winnie</dc:creator><author>Winnie</author><pubDate>Tue, 05 Jun 2012 01:16:00 GMT</pubDate><guid>http://www.cppblog.com/weining45/archive/2012/06/05/177596.html</guid><wfw:comment>http://www.cppblog.com/weining45/comments/177596.html</wfw:comment><comments>http://www.cppblog.com/weining45/archive/2012/06/05/177596.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/weining45/comments/commentRss/177596.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/weining45/services/trackbacks/177596.html</trackback:ping><description><![CDATA[<p style="text-indent: 18pt; margin: 0cm 0cm 6pt"><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">之前已经介绍了Windows程序的设计过程及关键，简单一句话就是把界面设计好，关键是做好交互操作的处理过程。</span></p>
<p style="text-indent: 18pt; margin: 0cm 0cm 6pt"><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">Window</span><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">程序设计的大框架基本固定：设计窗口类，注册，创建，显示，更新，消息循环。在这个过程中，设计、注册、创建、显示和更新窗体，包括消息循环过程等等所有操作都是调用API函数来完成的，所谓的API函数就是操作系统提供给应用程序的编程接口。</span></p>
<p style="text-indent: 18pt; margin: 0cm 0cm 6pt"><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">除了提供API函数库供编程使用，针对面向对象编程理念，微软还对这些API函数进行了封装，将其根据功能划分到各种C++类中。程序员通过调用类的成员函数来调用这些API函数。</span></p>
<p style="text-indent: 18pt; margin: 0cm 0cm 6pt"><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">MFC</span><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">就是微软为了简化程序员的开发工作所开发的一套C++类的集合。除了将函数封装到类，MFC还将每一个窗口应用程序都需要的步骤进行了封装，即将Window程序设计的大框架进行了封装。所以简单来说，MFC就是对API函数库和Windows程序设计过程进行了封装，目的是简化程序员的开发工作，但是对初学者却造成了很多不必要地困扰，在没有了解MFC的架构前看代码无从下手，找不到入口点。</span></p>
<p style="text-indent: 18pt; margin: 0cm 0cm 6pt"><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">下面以通过MFC AppWizard生成的单文档MFC应用程序为例来进行讲解，工程名为Test。自动生成的工程有五个类：CAboutDlg，CMainFrame，CTestApp，CTestDoc，CTestView，和一个CTestApp类型的全局对象theApp。</span></p>
<p style="text-indent: 18pt; margin: 0cm 0cm 6pt"><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">Windows</span><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">应用程序的消息处理过程中，操作系统是通过应用程序实例句柄来找到消息所属的应用程序，再通过窗口句柄进一步找到消息所属的窗口，进而调用其窗口过程函数。对于MFC程序而言，通过产生一个应用程序类CTestApp的对象theApp来唯一标识应用程序的实例。每个MFC程序有且仅有一个应用程序类（CWinApp）的派生类（CTestApp）。每个MFC程序实例有且仅有一个该派生类的实例化对象，即theApp全局对象。</span></p>
<p style="text-indent: 18pt; margin: 0cm 0cm 6pt"><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">全局变量是在main函数之前分配内存空间，所以在进入主函数之前，首先是创建CTestApp的对象。而CTestApp是CWinApp的子类，所以在创建该类的实例前，首先调用了CWinApp的构造函数。该函数完成程序运行时的一些初始化工作。</span></p>
<p style="text-indent: 18pt; margin: 0cm 0cm 6pt"><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">接着进入主函数。主函数中即完成窗体的设计、注册、创建、显示、更新等等一系列过程，并启动消息循环过程。具体表现就是调用theApp的InitApplication、InitInstance和Run函数。InitApplication函数完成MFC内部管理方面的工作。由CTestApp的父类CWinApp完成。InitInstance函数因为CTestApp重写之，所以调用的是CTestApp类内的函数实现。这两个函数完成了窗体的设计直到更新显示。而Run函数则启动了消息循环过程。</span></p>
<p style="text-indent: 18pt; margin: 0cm 0cm 6pt"><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">至此，MFC的大框架介绍完毕。</span></p>
<p style="text-indent: 18pt; margin: 0cm 0cm 6pt"><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">Windows</span><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">程序设计的关键是窗口过程函数的设计。一般的Windows应用程序在窗口过程函数中使用switch对消息进行判别并分类处理。而MFC的消息循环并不是采用Window应用程序所采用的方式，它采用专门的消息映射机制。</span></p>
<p style="text-indent: 18pt; margin: 0cm 0cm 6pt"><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">至于原因，个人总结是：在介绍一般Windows程序设计时，每个窗口对象对应一个窗口过程函数，该窗口的所有消息均由该函数进行处理；而MFC架构中采用了文档/视图结构，该架构由五个类共同实现：CAboutDlg，CMainFrame，CTestApp，CTestDoc，CTestView。该应用程序可能接收到的消息也被划分到不同的类中，并且该架构中类的继承层次较复杂，如果将类可能接收到的消息作为类的成员变量存在，类的体积将非常庞大，所以采用消息映射机制来实现。</span></p>
<p style="text-indent: 18pt; margin: 0cm 0cm 6pt"><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">MFC</span><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">消息映射机制的具体实现是：</span></p>
<p style="text-indent: 18pt; margin: 0cm 0cm 6pt"><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">在每个能接收和处理消息的类中，定义一个消息和消息函数静态对照表，即消息映射表。在消息映射表中，消息与对应的消息处理函数指针是成对出现的。某个类能处理的所有消息及其对应的消息处理函数的地址都列在这个类所对应的静态表中。当有消息需要处理时，程序只能搜索该消息静态表，查看表中是否含有该消息，就可知道该类能够处理此消息。如果能处理该消息，则同样依照静态表能很容易找到并调用对应的消息处理函数。</span></p><img src ="http://www.cppblog.com/weining45/aggbug/177596.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/weining45/" target="_blank">Winnie</a> 2012-06-05 09:16 <a href="http://www.cppblog.com/weining45/archive/2012/06/05/177596.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>入门1</title><link>http://www.cppblog.com/weining45/archive/2012/06/05/177594.html</link><dc:creator>Winnie</dc:creator><author>Winnie</author><pubDate>Tue, 05 Jun 2012 01:15:00 GMT</pubDate><guid>http://www.cppblog.com/weining45/archive/2012/06/05/177594.html</guid><wfw:comment>http://www.cppblog.com/weining45/comments/177594.html</wfw:comment><comments>http://www.cppblog.com/weining45/archive/2012/06/05/177594.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/weining45/comments/commentRss/177594.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/weining45/services/trackbacks/177594.html</trackback:ping><description><![CDATA[<p style="text-indent: 18pt; margin: 0cm 0cm 6pt"><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">Windows</span><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">应用程序至少有一个窗口，称为主窗口。Windows应用程序通过窗口和外界交互，是一种基于消息、事件驱动的设计模式，每个窗口对应一个窗口过程函数，负责对交互信息进行处理。应用程序的设计者可以根据需要设计窗口的特征。所以Windows应用程序的设计过程如下：</span></p>
<p style="text-indent: -21pt; margin: 0cm 0cm 6pt 42pt"><span style="font-family: Wingdings; font-size: 9pt">l<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp; </span></span><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">设计一个窗体类。指定该类窗体的特征，例如是否显示最大化/最小化框，指定窗口过程函数的地址（即指定窗体过程函数的名称）</span></p>
<p style="text-indent: -21pt; margin: 0cm 0cm 6pt 42pt"><span style="font-family: Wingdings; font-size: 9pt">l<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp; </span></span><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">注册窗体类。只有注册了该窗体类，后面创建窗体类对象时，系统才知道该窗体的特征。窗体类对象涉及到与系统的交互，所以需要向系统登记该类类型，而不是简单的设计类，创建对象。</span></p>
<p style="text-indent: -21pt; margin: 0cm 0cm 6pt 42pt"><span style="font-family: Wingdings; font-size: 9pt">l<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp; </span></span><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">创建窗口类对象。</span></p>
<p style="text-indent: -21pt; margin: 0cm 0cm 6pt 42pt"><span style="font-family: Wingdings; font-size: 9pt">l<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp; </span></span><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">显示并更新窗口类对象。窗口类对象创建之后，只是在内存中存在该对象，想要显示出来还需要调用其显示函数。一开始窗体类对象的界面属于无效状态，调用更新函数使其有效。当窗体部分或全部被挡住时，被挡住的部分就处于无效状态，想要再次显示需要更新之。</span></p>
<p style="text-indent: -21pt; margin: 0cm 0cm 6pt 42pt"><span style="font-family: Wingdings; font-size: 9pt">l<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp; </span></span><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">进行消息循环。即开启一个死循环处理应用程序中窗口的消息，即根据消息调用窗口过程函数。当窗口销毁时跳出该死循环，结束应用程序。</span></p>
<p style="text-indent: 18pt; margin: 0cm 0cm 6pt"><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">Windows</span><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">应用程序设计的关键是窗口过程函数的设计。</span></p>
<p>&nbsp;</p>
<p style="text-indent: 18pt; margin: 0cm 0cm 6pt"><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">Windows</span><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">应用程序的关键就是消息循环部分，借由消息队列存放捕获到的消息，然后循环从消息队列中取出消息进行处理。其消息处理过程如下：</span></p>
<p style="text-indent: -21pt; margin: 0cm 0cm 6pt 42pt"><span style="font-family: Wingdings; font-size: 9pt">l<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp; </span></span><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">操作系统接收到应用程序的窗口消息，例如捕获到用户在窗口单击鼠标左键事件，将消息投递到该应用程序的消息队列中；</span></p>
<p style="text-indent: -21pt; margin: 0cm 0cm 6pt 42pt"><span style="font-family: Wingdings; font-size: 9pt">l<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp; </span></span><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">应用程序在消息循环中调用GetMessage函数从消息队列中取出一条一条的消息。取出消息后，应用程序可以对消息进行一些预处理。例如，对于操作系统，鼠标按下和鼠标抬起分别对应LButtonDown和LButtonUp两个消息，应用程序将这两个消息处理为一个鼠标单击消息。</span></p>
<p style="text-indent: -21pt; margin: 0cm 0cm 6pt 42pt"><span style="font-family: Wingdings; font-size: 9pt">l<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp; </span></span><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">应用程序调用DispatchMessage，将消息回传给操作系统。</span></p>
<p style="text-indent: -21pt; margin: 0cm 0cm 6pt 42pt"><span style="font-family: Wingdings; font-size: 9pt">l<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp; </span></span><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">操作系统利用消息中包含的窗口过程函数指针调用窗口过程，对消息进行处理。</span></p>
<p style="text-indent: 18pt; margin: 0cm 0cm 6pt"><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">上述过程是一般的消息处理过程，其实消息又分为两类：进队消息和不进队消息，上述过程是进队消息的处理过程，不进队消息是操作系统捕获到消息后直接调用应用程序的处理过程，而不放入消息队列中。</span></p>
<p><span style="font-family: '微软雅黑','sans-serif'; font-size: 9pt">个人认为，进队消息应该是需要预处理的消息，因为最后还是回到操作系统，由操作系统调用窗口过程函数进行处理。至于为何非要回到操作系统，个人理解也和进队/不进队有关，因为有的消息不进队则直接由操作系统调用处理函数，所以进队的消息也统一再回到操作系统，由操作系统调用处理函数。所以在这里可以看出，程序员设计的窗口过程函数不是由应用程序调用，而是由操作系统调用，所以该函数类型为回调函数，即函数的调用方和函数的设计方不是同一人。</span></p>
<p>&nbsp;</p><img src ="http://www.cppblog.com/weining45/aggbug/177594.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/weining45/" target="_blank">Winnie</a> 2012-06-05 09:15 <a href="http://www.cppblog.com/weining45/archive/2012/06/05/177594.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>