﻿<?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++博客-C++乐园-文章分类-Windows编程</title><link>http://www.cppblog.com/killsound/category/3471.html</link><description>C/C++ 交流</description><language>zh-cn</language><lastBuildDate>Thu, 03 Dec 2009 07:03:56 GMT</lastBuildDate><pubDate>Thu, 03 Dec 2009 07:03:56 GMT</pubDate><ttl>60</ttl><item><title>VC 6.0中编译和使用OpenSSL的过程</title><link>http://www.cppblog.com/killsound/articles/102374.html</link><dc:creator>小不懂^_^</dc:creator><author>小不懂^_^</author><pubDate>Wed, 02 Dec 2009 02:51:00 GMT</pubDate><guid>http://www.cppblog.com/killsound/articles/102374.html</guid><wfw:comment>http://www.cppblog.com/killsound/comments/102374.html</wfw:comment><comments>http://www.cppblog.com/killsound/articles/102374.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/killsound/comments/commentRss/102374.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/killsound/services/trackbacks/102374.html</trackback:ping><description><![CDATA[<p class="g_p_center g_t_wrap g_t_left g_t_20 g_c_pdin c07" id=blogtitle_fks_085074092094082068093094084095081083084075087086085068 style="MARGIN: 20px auto 10px"><span style="FONT-FAMILY: '微软雅黑','黑体',Arial,Helvetica,Sans-Serif">VC 6.0中编译和使用OpenSSL的过程</span></p>
<div class=g_blog_list>
<div class="g_t_center g_c_pdin g_p_center c07 content" id=blogtext_fks_085074092094082068093094084095081083084075087086085068>
<div class=ns_content>
<p>一、编译并安装OpenSSL</p>
<p>1、按照标准步骤从源代码编译安装OpenSSL<br>&nbsp;&nbsp;&nbsp; 在编译OpenSSL前，需要正确安装Perl，因为在编译OpenSSL时需要使用到该程序。<br>&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; 下载最新版本的Perl：<a href="http://downloads.activestate.com/ActivePerl/Windows/"><font color=#6aa50d>http://downloads.activestate.com/ActivePerl/Windows/</font></a>。然后安装之。</p>
<p>&nbsp;&nbsp;&nbsp; 下载最新版本的OpenSSL：<a href="http://www.openssl.org/source/"><font color=#6aa50d>http://www.openssl.org/source/</font></a><br>&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; 然后将源码解压缩到某个目录（如 C:\openssl-0.9.8j）中。<br>&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; 进入openssl源码目录。<br>&nbsp;&nbsp;&nbsp; cd c:\openssl-0.9.8.j<br>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp; 以下为参照该目录下的文件INSTALL.W32的执行过程：</p>
<p>&nbsp;&nbsp;&nbsp; 运行configure：<br>&nbsp;&nbsp;&nbsp; perl Configure VC-WIN32 --prefix=c:/openssl</p>
<p>&nbsp;&nbsp;&nbsp; 创建Makefile文件：<br>&nbsp;&nbsp;&nbsp; ms\do_ms<br>&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; 编译动态库：<br>&nbsp;&nbsp;&nbsp; nmake -f ms\ntdll.mak<br>&nbsp;&nbsp;&nbsp; 编译静态库：<br>&nbsp;&nbsp;&nbsp; nmake -f ms\nt.mak</p>
<p>&nbsp;&nbsp;&nbsp; 测试动态库：<br>&nbsp;&nbsp;&nbsp; nmake -f ms\ntdll.mak test<br>&nbsp;&nbsp;&nbsp; 测试静态库：<br>&nbsp;&nbsp;&nbsp; nmake -f ms\nt.mak test</p>
<p>&nbsp;&nbsp;&nbsp; 安装动态库：<br>&nbsp;&nbsp;&nbsp; nmake -f ms\ntdll.mak install<br>&nbsp;&nbsp;&nbsp; 安装静态库：<br>&nbsp;&nbsp;&nbsp; nmake -f ms\nt.mak install</p>
<p>&nbsp;&nbsp;&nbsp; 清除上次动态库的编译，以便重新编译：<br>&nbsp;&nbsp;&nbsp; nmake -f ms\ntdll.mak clean<br>&nbsp;&nbsp;&nbsp; 清除上次静态库的编译，以便重新编译：<br>&nbsp;&nbsp;&nbsp; nmake -f ms\nt.mak clean</p>
<p>2、如果嫌麻烦，不想编译，可以直接用别人做好的windows OpenSSL 安装包（我用的是0.9.8j版），<br>&nbsp;&nbsp; 可以从 <a href="http://www.slproweb.com/products/Win32OpenSSL.html"><font color=#6aa50d>http://www.slproweb.com/products/Win32OpenSSL.html</font></a> 下载 OpenSSL for Windows，直接安装。</p>
<p>P.S. OpenSSL for Windows 的源代码有一些数据类型和VC6的编译器不兼容，我发现的不兼容的数据类型如下：<br>　　在OpenSSL安装目录的下的include/bn.h文件中，将<br>&nbsp;&nbsp;&nbsp; #define BN_ULLONG&nbsp;unsigned long long<br>&nbsp;&nbsp;&nbsp; #define BN_ULONG&nbsp;unsigned long long<br>&nbsp;&nbsp;&nbsp; #define BN_LONG&nbsp;long long<br>&nbsp;&nbsp;&nbsp; 分别修改为：<br>&nbsp;&nbsp;&nbsp; #define BN_ULLONG&nbsp;ULONGLONG<br>&nbsp;&nbsp;&nbsp; #define BN_ULONG&nbsp;ULONGLONG<br>&nbsp;&nbsp;&nbsp; #define BN_LONG&nbsp;LONGLONG</p>
<p>&nbsp;&nbsp;&nbsp; 否则，会出现编译错误。</p>
<p><br>二、使用OpenSSL</p>
<p>&nbsp;&nbsp;&nbsp; 在VC中配置使用以上的函数库：<br>&nbsp;&nbsp;&nbsp; 点击菜单：Tools -&gt; Options，弹出对话框"Options"，在该对话框中选择"Directories"标签。<br>&nbsp;&nbsp;&nbsp; 在"Show directories for:"的"Include files"选项中新增目录"C:\openssl\include"；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "Library files"选择中新增目录"C:\openssl\lib"。</p>
<p>&nbsp;&nbsp;&nbsp; 然后在需要链接OpenSSL函数库的工程中加入如下两句：<br>&nbsp;&nbsp;&nbsp; #pragma comment(lib, "ssleay32.lib")<br>&nbsp;&nbsp;&nbsp; #pragma comment(lib, "libeay32.lib")<br>&nbsp;&nbsp;&nbsp; 其作用是将OpenSSL所需的库导入工程中。</p>
<p>三、问题</p>
<p>&nbsp;&nbsp;&nbsp; 我在链接OpenSSL的静态函数库时遇到类似以下的问题：<br>&nbsp;&nbsp;&nbsp; Linking...<br>&nbsp;&nbsp;&nbsp; msvcrt.lib(MSVCRT.dll) : error LNK2005: _strchr already defined in libcmtd.lib(strchr.obj)<br>&nbsp;&nbsp;&nbsp; ...<br>&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; 这是由于OpenSSL的静态函数库使用的是了VC的多线程DLL的Release版本，而我的程序使用了多线程静态链接的Release版本。</p>
<p>&nbsp;&nbsp;&nbsp; 调整OpenSSL的静态函数库使用的库函数版本即可，调整过程如下：<br>&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; 编辑文件 ms\nt.mak，将该文件第19行<br>&nbsp;&nbsp;&nbsp; "CFLAG= /MD /Ox /O2 /Ob2 /W3 /WX /Gs0 /GF /Gy /nologo -DOPENSSL_SYSNAME_WIN32 -DWIN32_LEAN_AND_MEAN -DL_ENDIAN -DDSO_WIN32 -D_CRT_SECURE_NO_DEPRECATE -</p>
<p>D_CRT_NONSTDC_NO_DEPRECATE /Fdout32 -DOPENSSL_NO_CAMELLIA -DOPENSSL_NO_SEED -DOPENSSL_NO_RC5 -DOPENSSL_NO_MDC2 -DOPENSSL_NO_TLSEXT -DOPENSSL_NO_KRB5 -</p>
<p>DOPENSSL_NO_DYNAMIC_ENGINE"<br>&nbsp;&nbsp;&nbsp; 中的"/MD"修改为"/MT"。然后重新编译安装OpenSSL即可。</p>
<p>四、附录：在VC中对C/C++ 运行时库不同版本编译指令说明<br>&nbsp;&nbsp;&nbsp; 《在VC中对C/C++ 运行时库不同版本编译指令说明》(<a href="http://blog.yesky.com/271/dgkang/1737771.shtml"><font color=#6aa50d>http://blog.yesky.com/271/dgkang/1737771.shtml</font></a>)一文中详细介绍了连接不同版本库的编译指令如下：<br>&nbsp;&nbsp;&nbsp; C Runtime Library：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /MD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MSVCRT.LIB&nbsp;&nbsp;&nbsp;&nbsp; 多线程DLL的Release版本<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /MDd&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MSVCRTD.LIB&nbsp;&nbsp;&nbsp; 多线程DLL的Debug版本<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /MT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LIBCMT.LIB&nbsp;&nbsp;&nbsp;&nbsp; 多线程静态链接的Release版本<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /MTd&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LIBCMTD.LIB&nbsp;&nbsp;&nbsp; 多线程静态链接的Debug版本<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /clr&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MSVCMRT.LIB&nbsp;&nbsp;&nbsp; 托管代码和非托管代码混合<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /clr:pure&nbsp; MSVCURT.LIB&nbsp;&nbsp;&nbsp; 纯托管代码<br>&nbsp;&nbsp;&nbsp; C++ Standard Library：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /MD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MSVCPRT.LIB&nbsp;&nbsp;&nbsp;&nbsp; 多线程DLL的Release版本<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /MDd&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MSVCPRTD.LIB&nbsp;&nbsp;&nbsp; 多线程DLL的Debug版本<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /MT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LIBCPMT.LIB&nbsp;&nbsp;&nbsp;&nbsp; 多线程静态链接的Release版本<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /MTd&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LIBCPMTD.LIB&nbsp;&nbsp;&nbsp; 多线程静态链接的Debug版本</p>
</div>
</div>
</div>
<img src ="http://www.cppblog.com/killsound/aggbug/102374.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/killsound/" target="_blank">小不懂^_^</a> 2009-12-02 10:51 <a href="http://www.cppblog.com/killsound/articles/102374.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>(转)将 Win32 C/C++ 应用程序迁移到 POWER 上的 Linux，第 3 部分: 信号 </title><link>http://www.cppblog.com/killsound/articles/81951.html</link><dc:creator>小不懂^_^</dc:creator><author>小不懂^_^</author><pubDate>Tue, 05 May 2009 08:17:00 GMT</pubDate><guid>http://www.cppblog.com/killsound/articles/81951.html</guid><wfw:comment>http://www.cppblog.com/killsound/comments/81951.html</wfw:comment><comments>http://www.cppblog.com/killsound/articles/81951.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/killsound/comments/commentRss/81951.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/killsound/services/trackbacks/81951.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 将 Win32 C/C++ 应用程序迁移到 POWER 上的 Linux，第 3 部分: 信号                                                                                                                             ...&nbsp;&nbsp;<a href='http://www.cppblog.com/killsound/articles/81951.html'>阅读全文</a><img src ="http://www.cppblog.com/killsound/aggbug/81951.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/killsound/" target="_blank">小不懂^_^</a> 2009-05-05 16:17 <a href="http://www.cppblog.com/killsound/articles/81951.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>(转)将 Win32 C/C++ 应用程序迁移到 POWER 上的 Linux，第 2 部分: 互斥</title><link>http://www.cppblog.com/killsound/articles/81950.html</link><dc:creator>小不懂^_^</dc:creator><author>小不懂^_^</author><pubDate>Tue, 05 May 2009 08:14:00 GMT</pubDate><guid>http://www.cppblog.com/killsound/articles/81950.html</guid><wfw:comment>http://www.cppblog.com/killsound/comments/81950.html</wfw:comment><comments>http://www.cppblog.com/killsound/articles/81950.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/killsound/comments/commentRss/81950.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/killsound/services/trackbacks/81950.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 将 Win32 C/C++ 应用程序迁移到 POWER 上的 Linux，第 2 部分: 互斥                                                                                                                             ...&nbsp;&nbsp;<a href='http://www.cppblog.com/killsound/articles/81950.html'>阅读全文</a><img src ="http://www.cppblog.com/killsound/aggbug/81950.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/killsound/" target="_blank">小不懂^_^</a> 2009-05-05 16:14 <a href="http://www.cppblog.com/killsound/articles/81950.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>(转)Win32 C/C++ 应用程序迁移到 POWER 上的 Linux 第 1 部分: 进程、线程和共享内存服务</title><link>http://www.cppblog.com/killsound/articles/81949.html</link><dc:creator>小不懂^_^</dc:creator><author>小不懂^_^</author><pubDate>Tue, 05 May 2009 08:12:00 GMT</pubDate><guid>http://www.cppblog.com/killsound/articles/81949.html</guid><wfw:comment>http://www.cppblog.com/killsound/comments/81949.html</wfw:comment><comments>http://www.cppblog.com/killsound/articles/81949.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/killsound/comments/commentRss/81949.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/killsound/services/trackbacks/81949.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 将 Win32 C/C++ 应用程序迁移到 POWER 上的 Linux，第 1 部分: 进程、线程和共享内存服务                                                                                                                     ...&nbsp;&nbsp;<a href='http://www.cppblog.com/killsound/articles/81949.html'>阅读全文</a><img src ="http://www.cppblog.com/killsound/aggbug/81949.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/killsound/" target="_blank">小不懂^_^</a> 2009-05-05 16:12 <a href="http://www.cppblog.com/killsound/articles/81949.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>明明白白Inf文件</title><link>http://www.cppblog.com/killsound/articles/27064.html</link><dc:creator>小不懂^_^</dc:creator><author>小不懂^_^</author><pubDate>Wed, 27 Jun 2007 07:35:00 GMT</pubDate><guid>http://www.cppblog.com/killsound/articles/27064.html</guid><wfw:comment>http://www.cppblog.com/killsound/comments/27064.html</wfw:comment><comments>http://www.cppblog.com/killsound/articles/27064.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/killsound/comments/commentRss/27064.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/killsound/services/trackbacks/27064.html</trackback:ping><description><![CDATA[<strong>转载]明明白白Inf文件<br><br></strong><span class=tpc_content><font size=2>信息来源：网络<br><br>INF文件全称Information&nbsp;File文件，是Winodws操作系统下用来描述设备或文件等数据信息的文件。INF文件是由标准的ASCII码组成，您可以用任何一款文字编辑器查看修改其中的内容。一般我们总是认为INF文件是系统设备的驱动程序，其实这是错误的认识，Windows之所以在安装某些硬件的驱动时提示需要INF文件是因为INF文件为该设备提供了一个全面描述硬件参数和相应驱动文件(DLL文件)的信息。就好比我们看着说明书安装电脑硬件一样，我们就是Windows系统，说明书就是INF文件。INF文件功能非常强大，几乎能完成日常操作的所有功能。您可以把它看成是Windows系统底下的超强批初理。要熟练掌握和理解甚至是编写INF文件需要对其内部结构有相当的认识。下面就让我们来深入到INF文件中的内部一窥其真面貌吧！<br><br>INF文件的组成有节(Sections)，键(Key)和值(value)三部分。<br>关键节有<br>[Version]版本描述信息，主要用于版本控制。<br>[Strings]字符串信息，用于常量定义。<br>[DestinationDirs]定义系统路径信息。<br>[SourceDisksNames]指明源盘信息。<br>[SourceDisksNames]指明源盘文件名。<br>[DefaultInstall]开始执行安装。<br>其它的节可以自定义，下面用一实例来具体讲解。<br><br><br>程序代码<br>[Version]<br>Signature=$Chicago$<br>Provider=%Author%<br><br>[Strings]<br>Product="添加文件关联演示"<br>Version="1.0"<br>Author="Xunchi"<br>Copyright="Copyright&nbsp;2005"<br>CustomFile="inf"&nbsp;;修改您需要的文件名后缀<br>Program="NOTEPAD.EXE"&nbsp;;修改您需要关联的应用程序名<br><br>[Add.Reg]<br>HKCR,"."%CustomFile%,"",FLG_ADDREG_TYPE_SZ&nbsp;,%CustomFile%File<br>HKCR,%CustomFile%File,"",FLG_ADDREG_TYPE_SZ,安装信息<br>HKCR,%CustomFile%"File\shell","",FLG_ADDREG_TYPE_SZ,open<br>HKCR,%CustomFile%"File\shell\open\command","",FLG_ADDREG_TYPE_SZ,%program%&nbsp;%1<br><br>[DefaultInstall]<br>AddReg=Add.Reg<br><br>　　在[Version]节中"Signature"项定义了该INF文件需要运行在何种操作系统版本中。有$Windows&nbsp;NT$,&nbsp;$Chicago$,&nbsp;or&nbsp;$Windows&nbsp;95$三个值供选择，一般选择$Chicago$即可。项Provider中定义了该文件的创作来源，%Author%指引用Author项的值。您也可自定其它项来描述该INF文件的版本信息。该INF文件的作用是关联文件，所以主要是对注册表的操作，我们来看[Add.Reg]节，共四条语句，格式都是一样。HKCR表示根HKEY_CLASSES_ROOT，第二个参数是子键的路径名，第三个参数是表明值的类型，最后是值(具体见附表)。以上都是对操作的定义与过程，在节[DefaultInstall]中是开始执行要安装的流程，AddReg表明是对注册表进行操作，操作对象是Add.Reg节中的定义。如果您把AddReg换成DelReg则是删除注册表中的键值。当鼠标单击该INF文件在弹出的菜单中选择&#8220;安装&#8221;就开始执行您所定义的操作。该示例在系统的INF文件右键菜单中增加了查看编辑功能并设置了默认动作，因为在安装了不了解的INF文件有可能对系统产生不良的影响，这样双击文件就可打开编辑该文件了。<br><br><br>　　再看看INF文件在文件操作方面的能力吧。请看下面的一个例子。<br><br>程序代码<br>[Version]<br>Signature=$Chicago$<br>Provider=%Author%<br>[Strings]<br>Product="文件复制和安装演示"<br>Version="1.0"<br>Author="Xunchi"<br>Copyright="Copyright&nbsp;2005"<br><br>[FileList]<br>ProcessList.exe&nbsp;;此文件已在当前目录下，下同。<br><br>[FileList1]<br>Wordpad.exe<br>[DestinationDirs]<br>FileList=11&nbsp;;安装到Windows的系统目录<br>FileList1=10&nbsp;;安装到Windows目录<br>[DefaultInstall]<br>Copyfiles=FileList,FileList1<br><br>　　相同的节的作用与上一例类似，请注意新出现的节[FileList]，这是我自定义的节名，它表示了一个文件组，[FileList1]也类似。在节[DestinationDirs]中需定义每个文件组复制到的目录(各个常量的意义见附表)。Copyfiles指明了需要进行复制的文件组。<br>　　INF文件的操作还包括服务(NT系统)程序的安装和卸载，INI文件的转换等。由于这些操作都比较的复杂和繁琐，且有一定的危险性故下次有机会再向大家进行深入探讨。<br>　　最后我们来看一下INF文件的执行机制，这时你也许要问不就是简单的执行一下&#8220;安装&#8221;吗？知其然不知其所以然知识水平是不会提高的。在&#8220;文件夹选项&#8221;中的&#8220;文件类型&#8221;找到INF文件的&#8220;安装&#8221;命令看到一串命令。&#8220;rundll32.exe&nbsp;setupapi,InstallHinfSection&nbsp;DefaultInst_all&nbsp;132&nbsp;%1&#8221;它表示了运行Dll文件setupapi.dll中的命令InstallHinfSection并传递给它起始节的名字&nbsp;DefaultInstall。可见起始节是可以自定义的。INF文件的执行也可用在各种支持API调用的编程工具中。至此INF文件的结构和运行机制我们已基本了解，现在就让你的思维开动起来，让它更好的为我们工作吧。<br><br><br>注册表操作的常量定义：<br>----------------------------------------------------------<br>常量&nbsp;根值&nbsp;<br>HKCR&nbsp;HKEY_CLASSES_ROOT.<br>HKCU&nbsp;HKEY_CURRENT_USER.<br>HKLM&nbsp;HKEY_LOCAL_MACHINE.<br>HKU&nbsp;HKEY_USERS.<br>-----------------------------------------------------------&nbsp;<br>FLG_ADDREG_APPEND&nbsp;在多字符串后添加字符<br>FLG_ADDREG_TYPE_SZ&nbsp;字符类型<br>FLG_ADDREG_TYPE_MULTI_SZ&nbsp;字符串类型<br>FLG_ADDREG_TYPE_EXPAND_SZ&nbsp;扩展字符串类型<br>FLG_ADDREG_TYPE_BINARY&nbsp;二进制值<br>FLG_ADDREG_TYPE_DWORD&nbsp;DWord值<br>FLG_ADDREG_TYPE_NONE&nbsp;NULL值<br>----------------------------------------------------------<br><br><br>[DestinationDirs]节中所定义的常量路径<br>----------------------------------------------------------<br>01&nbsp;源目录(后跟路径)<br>10&nbsp;Windows目录<br>11&nbsp;Windows系统目录<br>12&nbsp;驱动目录<br>17&nbsp;INF文件目录<br>18&nbsp;帮助文件目录<br>20&nbsp;字体目录<br>21&nbsp;根目录<br>24&nbsp;应用程序目录<br>25&nbsp;共享目录<br>30&nbsp;当前根目录<br>50&nbsp;System目录<br>51&nbsp;Spool&nbsp;目录<br>52&nbsp;Spool&nbsp;驱动目录<br>53&nbsp;用户配置目录<br>----------------------------------------------------------<br><br>[DefaultInstall]节中定义的操作<br>----------------------------------------------------------<br>LogConfig&nbsp;Log日志文件配置<br>Copyfiles&nbsp;复制文件<br>Renfiles&nbsp;文件改名<br>Delfiles&nbsp;删除文件<br>UpdateInis&nbsp;更新Inis<br>UpdateIniFields&nbsp;更新Ini字段<br>AddReg&nbsp;添加注册项<br>DelReg&nbsp;删除注册项<br>Ini2Reg&nbsp;Ini文件转换为Reg文件<br>-----------------------------------------------------------</font></span>
<img src ="http://www.cppblog.com/killsound/aggbug/27064.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/killsound/" target="_blank">小不懂^_^</a> 2007-06-27 15:35 <a href="http://www.cppblog.com/killsound/articles/27064.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>MDI应用程序剖析(转)</title><link>http://www.cppblog.com/killsound/articles/20375.html</link><dc:creator>小不懂^_^</dc:creator><author>小不懂^_^</author><pubDate>Thu, 22 Mar 2007 11:34:00 GMT</pubDate><guid>http://www.cppblog.com/killsound/articles/20375.html</guid><wfw:comment>http://www.cppblog.com/killsound/comments/20375.html</wfw:comment><comments>http://www.cppblog.com/killsound/articles/20375.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/killsound/comments/commentRss/20375.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/killsound/services/trackbacks/20375.html</trackback:ping><description><![CDATA[使用AppWizard创建一个MDI应用程序，我创建的应用程序 叫MDITest，这样MFC生成了如下的类： <br />类名 作用 <br />CMDITestApp 派生于CWinApp的应用程序类。 <br />CMainFrame 派生于CMDIFrameWnd的MDI框架窗口类。 <br />CMDITestDoc 派生于CDocument的文档类。 <br />CChildFrame 派生于CMDIChildWnd的MDI子窗口类。 <br />CMDITestView 派生于CView的文档显示类。 <br />在运行时刻，CMainFrame, CChildFrame, CMDITestView的窗口关系如下面的表格示出： <br />CMainFrame <br />（Menu, Toolbar …） <br />MDIClient <br />  <br />CChildFrame <br />CMDITestView <br />   pDocument = *CMDITestDoc   (带有文档的指针) <br />  <br />  <br />  <br />  <br />  <br />[StatusBar] <br />其中，最外层的是顶层窗口CMainFrame，里面包含一个MDIClient窗 口。CChildFrame做为子窗口包含于MDIClient中（可以包含多 个），CChildFrame里面则是真实的文档表示窗口CMDITestView了。 <br />  <br />我们从这里开始： <br />// CMDITestApp 初始化 <br />BOOL CMDITestApp::InitInstance() <br />  <br />做为CWinApp的派生类，通常需要重载InitInstance(), ExitInstance()两个函数，以完成应 用的初始化和退出。我们现在关心InitInstance中关于文档模板、窗口处理的部分，而忽略掉一些CommonControl, OLE初始化部分。 <br />  <br />整个InitInstance代码如下： <br />BOOL CMDITestApp::InitInstance() <br />{ <br />     InitCommonControls();       // 这里删减了大量注释和错误处理 <br />     CWinApp::InitInstance(); <br />     AfxOleInit(); <br />     AfxEnableControlContainer(); <br />     SetRegistryKey(_T("应用程序向导生成的本地应用程序"<a href="http://www.blogcn.com/images/wink.gif" target="_blank"><img src="http://www.blogcn.com/images/wink.gif" onload="if(this.width&gt;screen.width/2)this.style.width=screen.width/2;" border="0" /></a>); <br />     LoadStdProfileSettings(4);  // 加载标准 INI 文件选项(包括 MRU) <br />  <br />     TRACE("Before CMultiDocTemplate\n"<a href="http://www.blogcn.com/images/wink.gif" target="_blank"><img src="http://www.blogcn.com/images/wink.gif" onload="if(this.width&gt;screen.width/2)this.style.width=screen.width/2;" border="0" /></a>; <br />     // 注册应用程序的文档模板。文档模 板 <br />     // 将用作文档、框架窗口和视图之间的连 接 <br />     CMultiDocTemplate* pDocTemplate; <br />     pDocTemplate = new CMultiDocTemplate(IDR_MDITestTYPE, <br />         RUNTIME_CLASS(CMDITestDoc), <br />         RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架 <br />         RUNTIME_CLASS(CMDITestView)); <br />     if (!pDocTemplate) <br />         return FALSE; <br />     TRACE("Before AddDocTemplate\n"<a href="http://www.blogcn.com/images/wink.gif" target="_blank"><img src="http://www.blogcn.com/images/wink.gif" onload="if(this.width&gt;screen.width/2)this.style.width=screen.width/2;" border="0" /></a>; <br />     AddDocTemplate(pDocTemplate); <br />  <br />     // 创建主 MDI 框架窗口 <br />     TRACE("Before new CMainFrame\n"<a href="http://www.blogcn.com/images/wink.gif" target="_blank"><img src="http://www.blogcn.com/images/wink.gif" onload="if(this.width&gt;screen.width/2)this.style.width=screen.width/2;" border="0" /></a>; <br />     CMainFrame* pMainFrame = new CMainFrame; <br />     TRACE("Before pMainFrame-&gt;LoadFrame\n"<a href="http://www.blogcn.com/images/wink.gif" target="_blank"><img src="http://www.blogcn.com/images/wink.gif" onload="if(this.width&gt;screen.width/2)this.style.width=screen.width/2;" border="0" /></a>; <br />     if (!pMainFrame || !pMainFrame-&gt;LoadFrame(IDR_MAINFRAME)) <br />         return FALSE; <br />     m_pMainWnd = pMainFrame; <br />  <br />     TRACE("Before ParseCommandLine\n"<a href="http://www.blogcn.com/images/wink.gif" target="_blank"><img src="http://www.blogcn.com/images/wink.gif" onload="if(this.width&gt;screen.width/2)this.style.width=screen.width/2;" border="0" /></a>; <br />     CCommandLineInfo cmdInfo; <br />     ParseCommandLine(cmdInfo); <br />  <br />     // 调度在命令行中指定的命令。如 果 <br />     // 用 /RegServer、/Register、/Unregserver 或 /Unregister 启动应用程序，则返回 FALSE。 <br />     TRACE("Before ProcessShellCommand\n"<a href="http://www.blogcn.com/images/wink.gif" target="_blank"><img src="http://www.blogcn.com/images/wink.gif" onload="if(this.width&gt;screen.width/2)this.style.width=screen.width/2;" border="0" /></a>; <br />     if (!ProcessShellCommand(cmdInfo)) <br />         return FALSE; <br />  <br />     TRACE("Before pMainFrame-&gt;ShowWindow\n"<a href="http://www.blogcn.com/images/wink.gif" target="_blank"><img src="http://www.blogcn.com/images/wink.gif" onload="if(this.width&gt;screen.width/2)this.style.width=screen.width/2;" border="0" /></a>; <br />     // 主窗口已初始化，因此显示它并对其进行更 新 <br />     pMainFrame-&gt;ShowWindow(m_nCmdShow); <br />     TRACE("Before pMainFrame-&gt;UpdateWindow\n"<a href="http://www.blogcn.com/images/wink.gif" target="_blank"><img src="http://www.blogcn.com/images/wink.gif" onload="if(this.width&gt;screen.width/2)this.style.width=screen.width/2;" border="0" /></a>; <br />     pMainFrame-&gt;UpdateWindow(); <br />     return TRUE; <br />} <br />  <br />为了研究整个创建过程，我在其中添加了一些TRACE来跟踪创建顺序。 <br />  <br />忽略掉开始的乱七八糟的初始化，从CMultiDocTemplate开始： <br />     CMultiDocTemplate* pDocTemplate = new CMultiDocTemplate(IDR_MDITestTYPE, <br />         RUNTIME_CLASS(CMDITestDoc), <br />         RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架 <br />         RUNTIME_CLASS(CMDITestView)); <br />     AddDocTemplate(pDocTemplate); <br />(作了一点点简化) <br />这里首先创建了一个CMultiDocTemplate -- new CMainFrame; <br />     if (!pMainFrame || !pMainFrame-&gt;LoadFrame(IDR_MAINFRAME)) <br />         return FALSE; <br />  <br /><br />其中，需要研究的是LoadFrame的实现，以及里面都做了些什么。我们稍后研究。 <br />  <br />处理命令行，在这里第一个空文档被建立出来： <br />     CCommandLineInfo cmdInfo; <br />     ParseCommandLine(cmdInfo); <br />  <br />     // 调度在命令行中指定的命令。如果用 /RegServer、/Register、/Unregserver 或 /Unregister 启动应用程序，则返回 FALSE。 <br />     if (!ProcessShellCommand(cmdInfo))               // ? 这里创建出初始空文 档 <br />         return FALSE; <br />  <br />我们一会会重点研究ProcessShellCommand。 <br />  <br />最后，显示主窗口： <br />     pMainFrame-&gt;ShowWindow(m_nCmdShow); <br />     pMainFrame-&gt;UpdateWindow(); <br />  <br />至此，WinApp::InitInstance()完成了自己的工作。 <br />  <br />上面遗留了三个待研究的分支，让我们现在去研究它们： <br />1、  CDocTemplate <br />2、  CFrameWnd::LoadFrame <br />3、  CWnd::ProcessShellCommand <br />  <br />  <br />  <br />研究CDocTemplate <br />  <br />我们的例子中是构造了一个CMultiDocTemplate，它是从CDocTemplate派生而来，所以我们主 要研究CDocTemplate。 <br />CDocTemplate的几个关键属性列表如下： <br />     CRuntimeClass* m_pDocClass;         // class for creating new documents <br />     CRuntimeClass* m_pFrameClass;       // class for creating new frames <br />     CRuntimeClass* m_pViewClass;        // class for creating new views <br />  <br />其中： <br />m_pDocClass 表示文档类类型，在此例子中就是CMDITestDoc <br />m_pFrameClass 表示容纳View窗口的框架窗口类类型，此例中为CChildFrame <br />m_pViewClass 表示显示文档的View视类类型，此例中为CMDITestView <br />  <br />我们可以这样认为，CDocTemplate用于描述Frame-View-Doc的关系。当然它还有一大堆别的属 性，我们暂时先忽略。 <br />  <br />一会还会看到CDocTemplate的创建文档、框架、视的过程，放在ProcessShellCommand中研究。 <br />  <br />  <br />研究LoadFrame <br />  <br />让我们继续研究CFrameWnd::LoadFrame是怎么运作的。使用的方法是跟踪进入。。。 <br />BOOL CMDIFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, <br />     CWnd* pParentWnd, CCreateContext* pContext) <br />{ <br />     // 调用基类 CFrameWnd 的 LoadFrame, pContext 在创建主窗口时 = NULL <br />     //   pParentWnd = NULL <br />     if (!CFrameWnd::LoadFrame(nIDResource, dwDefaultStyle, <br />       pParentWnd, pContext)) <br />         return FALSE; <br />  <br />     // save menu to use when no active MDI child window is present <br />     ASSERT(m_hWnd != NULL); <br />     // 主窗口带有菜单，所以。。。 <br />     m_hMenuDefault = ::GetMenu(m_hWnd); <br />     if (m_hMenuDefault == NULL) <br />         TRACE(traceAppMsg, 0, "Warning: CMDIFrameWnd without a default menu.\n"<a href="http://www.blogcn.com/images/wink.gif" target="_blank"><img src="http://www.blogcn.com/images/wink.gif" onload="if(this.width&gt;screen.width/2)this.style.width=screen.width/2;" border="0" /></a>; <br />     return TRUE; <br />} <br />注意，我们的MDITest Application的主窗口CMainFrame是 从CMDIFrameWnd派生的，所以进入到这里，参考代码中红色的注释部分。继续跟踪进入CFrameWnd::LoadFrame。 <br />  <br />BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, <br />     CWnd* pParentWnd, CCreateContext* pContext) <br />{ <br />     // only do this once <br />     ASSERT_VALID_IDR(nIDResource);    // nIDResource = 128, IDR_MAINFRAME <br />     ASSERT(m_nIDHelp == 0 || m_nIDHelp == nIDResource); <br />  <br />     m_nIDHelp = nIDResource;    // ID for help context (+HID_BASE_RESOURCE) <br />  <br />     CString strFullString; <br />     if (strFullString.LoadString(nIDResource))  // = "MDITest" <br />         AfxExtractSubString(m_strTitle, strFullString, 0);    // 取得第一个子串 <br />  <br />     VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG)); <br />  <br />     // attempt to create the window <br />     // GetIconWndClass 会调用 virtual PreCreateWindow 函数，别处也会调用，从而 <br />     // 使得子类的PreCreateWindow 将被调用多次 <br />     LPCTSTR lpszClass = GetIconWndClass(dwDefaultStyle, nIDResource); <br />     CString strTitle = m_strTitle; <br />     // 调用 CFrameWnd::Create() 实际创建出窗 口。 <br />     // 注意：在这里将给 CMainFrame 发送 WM_CREATE 等多个消息。 触发 CMainFrame 的 <br />     //   OnCreate 处理等。 <br />     if (!Create(lpszClass, strTitle, dwDefaultStyle, rectDefault, <br />       pParentWnd, MAKEINTRESOURCE(nIDResource), 0L, pContext)) <br />     { <br />         return FALSE;   // will self destruct on failure normally <br />     } <br />  <br />     // save the default menu handle， 好像 CMDIFrameWnd 也保存了一次？ <br />     ASSERT(m_hWnd != NULL); <br />     m_hMenuDefault = ::GetMenu(m_hWnd); <br />  <br />     // load accelerator resource <br />     LoadAccelTable(MAKEINTRESOURCE(nIDResource)); <br />  <br />     // WM_INITIALUPDATE 是 MFC 发明的消息，参见后 面的说明。 <br />     if (pContext == NULL)   // send initial update <br />         SendMessageToDescendants(WM_INITIALUPDATE, 0, 0, TRUE, TRUE); <br />  <br />     return TRUE; <br />} <br />  <br />以下是从TN024: MFC-Defined Messages And Resources中抽取的部分说明： <br />WM_INITIALUPDATE <br />This message is sent by the document template to all descendants of a frame window when it is safe for them to do their initial update. It maps to a call to CView::OnInitialUpdate but can be used in other CWnd-derived classes for other one-shot updating. <br />wParam Not used (0) <br />lParam Not used (0) <br />returns Not used (0) <br /><br />  <br />归纳一下，LoadFrame中进行了如下事情： <br />1、  注册窗口类(AfxDeferRegisterClass) <br />2、  实际创建窗口(Create) <br />3、  处理菜单、快捷键，发送WM_INITIALUPDATE消息给所有子窗口。实际将 在CView中处理此消息。（例如：在ToolBar上面放一 个FormView，可能就能收到这个消息并处利？） <br />  <br />至此，CMainFrame已经成功创建，菜单已经装载，工具条、状态行等已经在CMainFrame::OnCreate中创建。让我们接着研究第一个子窗口是怎么被创建出来的，该过程和CMainFrame::LoadFrame比起来就不那么直接了。 <br />  <br />  <br />研究CWnd::ProcessShellCommand <br />  <br />第一个MDI子窗口是从这里面建立出来的，这实在是缺乏直观性。不过MFC就是这样，没办法。  <br />BOOL CWinApp::ProcessShellCommand(CCommandLineInfo&amp; rCmdInfo) <br />{ <br />     BOOL bResult = TRUE; <br />     switch (rCmdInfo.m_nShellCommand) <br />     { <br />     case CCommandLineInfo::FileNew: <br />         if (!AfxGetApp()-&gt;OnCmdMsg(ID_FILE_NEW, 0, NULL, NULL))      // 关键是这里 <br />              OnFileNew(); <br />         if (m_pMainWnd == NULL) <br />              bResult = FALSE; <br />         break; <br />  <br />     case CCommandLineInfo::FileOpen:                // 忽 略 <br />     case CCommandLineInfo::FilePrintTo:            // 忽略 <br />     case CCommandLineInfo::FilePrint: <br />     case CCommandLineInfo::FileDDE: <br />     case CCommandLineInfo::AppRegister: <br />     case CCommandLineInfo::AppUnregister: <br />     } <br />     return bResult; <br />} <br />进入到ProcessShellCommand，要处理很多种不同命令，我们忽略其它命令，单独看FileNew部分。 <br />注意：实际进入到了AfxGetApp()-&gt;OnCmdMsg(ID_FILE_NEW, 0, NULL, NULL)之中。 <br />  <br />AfxGetApp()实际返回了CMDITestApp的唯一实例，它从CWinApp - CWinThread - CCmdTarget - CObject 派生而来。我们没有重载OnCmdMsg，所以进入到CCmdTarget的OnCmdMsg处理中。为了研究，我们删减了一些代码。 <br />BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra, <br />     AFX_CMDHANDLERINFO* pHandlerInfo) <br />{ <br />     // 这里删减了一些代码 <br />     // determine the message number and code (packed into nCode) <br />     const AFX_MSGMAP* pMessageMap; <br />     const AFX_MSGMAP_ENTRY* lpEntry; <br />     UINT nMsg = 0; <br />     // 这里删减了一些代码，处理后 nMsg = WM_COMMAND <br />     // 为了简化，删减了一些断言等。以下循环用于查找处理此消息的 入口。 <br />     for (pMessageMap = GetMessageMap(); pMessageMap-&gt;pfnGetBaseMap != NULL; <br />       pMessageMap = (*pMessageMap-&gt;pfnGetBaseMap)()) <br />     { <br />         lpEntry = AfxFindMessageEntry(pMessageMap-&gt;lpEntries, nMsg, nCode, nID); <br />         if (lpEntry != NULL) <br />         { <br />              // 找到了消息处理项入口，分发此消息。 <br />              return _AfxDispatchCmdMsg(this, nID, nCode, <br />lpEntry-&gt;pfn, pExtra, lpEntry-&gt;nSig, pHandlerInfo); <br />         } <br />     } <br />     return FALSE;   // 未找到则不处理 <br />} <br />最终MFC很愉快地找到了一个入口项，       CWinApp::OnFileNew(void)       要处理这个消息。继续进入到_AfxDispatchCmdMsg中去看看。 <br />  <br />AFX_STATIC BOOL AFXAPI _AfxDispatchCmdMsg(CCmdTarget* pTarget, UINT nID, int nCode, <br />     AFX_PMSG pfn, void* pExtra, UINT_PTR nSig, AFX_CMDHANDLERINFO* pHandlerInfo) <br />         // return TRUE to stop routing <br />{ <br />     union MessageMapFunctions mmf; <br />     mmf.pfn = pfn; <br />     BOOL bResult = TRUE; // default is ok <br />  <br />     if (pHandlerInfo != NULL) <br />     { <br />         // just fill in the information, don't do it <br />         pHandlerInfo-&gt;pTarget = pTarget; <br />         pHandlerInfo-&gt;pmf = mmf.pfn; <br />         return TRUE; <br />     } <br />  <br />     switch (nSig) <br />     { <br />     case AfxSigCmd_v: <br />         // normal command or control notification <br />         ASSERT(CN_COMMAND == 0);        // CN_COMMAND same as BN_CLICKED <br />         ASSERT(pExtra == NULL); <br />         (pTarget-&gt;*mmf.pfnCmd_v_v)();         // ? 实际调用 pTarget 指向 的这个成员函数 <br />         break; <br />     // 下面还有大量的多种 AfxSigCmd_xxx，忽略掉它们。 <br />     default:    // illegal <br />         ASSERT(FALSE); return 0; break; <br />     } <br />     return bResult; <br />} <br />  <br />其中 (pTarget-&gt;*mmf.pfn_Cmd_v_v)() 对CWinApp::OnFileNew() 产生调 用，pTarget = CMDITestApp类实例。调用进入如下： <br />  <br />void CWinApp::OnFileNew() <br />{ <br />     if (m_pDocManager != NULL) <br />         m_pDocManager-&gt;OnFileNew(); <br />} <br />  <br />进入进入到CDocManager::OnFileNew() <br />  <br />void CDocManager::OnFileNew() <br />{ <br />     if (m_templateList.IsEmpty()) <br />          // 提示没有模板并返回 <br />     CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetHead();    // 第一个 <br />     if (m_templateList.GetCount() &gt; 1) <br />          // 弹出一个对话框（很难看的）提示用户选择一个文档模板 <br />  <br />     // 在这个例子里面，pTemplate 就是 CMDITestApp::InitInstance() 里面创建的那个模板 <br />     pTemplate-&gt;OpenDocumentFile(NULL); <br />} <br />  <br />在进入CMultiDocTemplate::OpenDocumentFile之前，我观察了一下调用堆栈，结果如下： <br />&gt;   mfc71d.dll!CDocManager::OnFileNew()  852  C++ <br />    mfc71d.dll!CWinApp::OnFileNew()  行 25   C++ <br />    mfc71d.dll!_AfxDispatchCmdMsg(CCmdTarget * pTarget=0x0042cae8, unsigned int nID=57600, int nCode=0, void (void)* pfn=0x0041153c, void * pExtra=0x00000000, unsigned int nSig=53, AFX_CMDHANDLERINFO * pHandlerInfo=0x00000000)  行89   C++ <br />    mfc71d.dll!CCmdTarget::OnCmdMsg(unsigned int nID=57600, int nCode=0, void * pExtra=0x00000000, AFX_CMDHANDLERINFO * pHandlerInfo=0x00000000)  396 + 0x27    C++ <br />    mfc71d.dll!CWinApp::ProcessShellCommand(CCommandLineInfo &amp; rCmdInfo={...})  行27 + 0x1e C++ <br />    MDITest.exe!CMDITestApp::InitInstance()  行 101 + 0xc    C++ <br />希望我还没有迷路：） <br />  <br />  <br />CMultiDocTemplate::OpenDocumentFile 又是很多很多代码，让我们选择一些。 <br />CDocument* CMultiDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName, <br />     BOOL bMakeVisible) <br />{ <br />     // 以下代码删减了验证、断言部 分 <br />     CDocument* pDocument = CreateNewDocument();              // 创建文档对象 <br />     CFrameWnd* pFrame = CreateNewFrame(pDocument, NULL);    // 创建框架窗口 <br />  <br />     if (lpszPathName == NULL) <br />     { <br />         pDocument-&gt;OnNewDocument();           // 初始化文档 <br />     } <br />     else <br />          // 打开已有文档 <br />  <br />     InitialUpdateFrame(pFrame, pDocument, bMakeVisible); <br />     return pDocument; <br />} <br />  <br />  <br />看一看CreateNewDocument() <br />CDocument* CDocTemplate::CreateNewDocument() <br />{ <br />     // default implementation constructs one from CRuntimeClass <br />     if (m_pDocClass == NULL) <br />          // 错误提示啦 <br />     // CRuntimeClass* m_pDocClass -&gt; CreateObject 实例化文档 类。 <br />     // 在此例子中既是 CMDITestDoc <br />     CDocument* pDocument = (CDocument*)m_pDocClass-&gt;CreateObject(); <br />     AddDocument(pDocument);      // 添加到模板里的文档列表，MultiDocTemplate 保存此一文 档 <br />     return pDocument; <br />} <br />  <br />  <br />CMDITestDoc有如下的定义，仅能从CRuntimeClass里面创建的。 <br />class CMDITestDoc : public CDocument <br />{ <br />protected: // 仅从序列化创建 <br />     CMDITestDoc();               // 被保护的构造函数 <br />     DECLARE_DYNCREATE(CMDITestDoc)             // 支持从 CRuntimeClass 信息中创建。 <br />  <br />  <br />再接着进行CreateNewFrame。 <br />CFrameWnd* CDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther) <br />{ <br />     // create a frame wired to the specified document <br />     CCreateContext context;           // 这个 CreateContext 传递到 LoadFrame 中 <br />     context.m_pCurrentFrame = pOther;         // 此例中 = NULL <br />     context.m_pCurrentDoc = pDoc;              // = 刚才创建的文档 <br />     context.m_pNewViewClass = m_pViewClass;   // 显示此文档的视类的类型 <br />     context.m_pNewDocTemplate = this; <br />  <br />     if (m_pFrameClass == NULL) <br />          // 提示错误并返回 <br />     // 利用 CRuntimeClass 信息创建框架窗口对象，此例中为 CChildFrame <br />     CFrameWnd* pFrame = (CFrameWnd*)m_pFrameClass-&gt;CreateObject(); <br />  <br />     // 这里，我们又看到了 LoadFrame , 参考前面的 LoadFrame 吧 <br />     // 在这里面，View 窗口也被产生出来。参考 TRACE 输 出。 <br />     pFrame-&gt;LoadFrame(m_nIDResource, <br />              WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE,   // default frame styles <br />              NULL, &amp;context); <br />     return pFrame; <br />} <br />  <br />  <br />LoadFrame之后View窗口将被创建出来，接着进入 到CMDITestDoc::OnNewDocument中，现在仅仅是一个空的函数，没有特定代码。 <br />BOOL CMDITestDoc::OnNewDocument() <br />{ <br />   TRACE("CMDITestDoc::OnNewDocument() entry\n"<a href="http://www.blogcn.com/images/wink.gif" target="_blank"><img src="http://www.blogcn.com/images/wink.gif" onload="if(this.width&gt;screen.width/2)this.style.width=screen.width/2;" border="0" /></a>; <br />     if (!CDocument::OnNewDocument()) <br />         return FALSE; <br />  <br />     // TODO: 在此添加重新初始化代 码 <br />     // (SDI 文档将重用该文档) <br />  <br />     return TRUE; <br />} <br />  <br />最后是CDocTemplate::InitialUpdateFrame，这里面主要是激活新建的框架、文档、视，看得挺头疼的。 <br />void CDocTemplate::InitialUpdateFrame(CFrameWnd* pFrame, CDocument* pDoc, <br />     BOOL bMakeVisible) <br />{ <br />     // just delagate to implementation in CFrameWnd <br />     pFrame-&gt;InitialUpdateFrame(pDoc, bMakeVisible); <br />} <br />  <br />现在，文档、框架窗口、视窗口全部被创建出来，我们胜利的返回到ProcessShellCommand处。显示和更新主窗口，完成了WinApp::InitInstance ： <br />     // 主窗口已初始化，因此显示它并对其进行更 新 <br />     pMainFrame-&gt;ShowWindow(m_nCmdShow); <br />     pMainFrame-&gt;UpdateWindow(); <br />  <br />  <br />  <br />看一下至此的TRACE输出，中间的DLL加载被去掉了： <br />Before CMultiDocTemplate <br />Before AddDocTemplate <br />Before new CMainFrame <br />CMainFrame::CMainFrame() <br />Before pMainFrame-&gt;LoadFrame <br />CMainFrame::PreCreateWindow entry         // 注意：PreCreateWindow 被两次调用 <br />CMainFrame::PreCreateWindow entry <br />CMainFrame::OnCreate entry before CMDIFrameWnd::OnCreate <br />CMainFrame::OnCreate before m_wndToolBar.CreateEx <br />CMainFrame::OnCreate before m_wndStatusBar.Create <br />Before ParseCommandLine <br />Before ProcessShellCommand <br />CMDITestDoc::CMDITestDoc()       // 文档对象被创建 <br />CChildFrame::CChildFrame()       // 子框架窗口被创建 <br />CChildFrame::PreCreateWindow entry  <br />CChildFrame::PreCreateWindow entry <br />CChildFrame::PreCreateWindow entry <br />CMDITestView::CMDITestView() entry   // 子框架窗口的 OnCreate 中创建了 View 窗口 <br />CMDITestView::PreCreateWindow entry <br />CMDITestDoc::OnNewDocument() entry <br />Before pMainFrame-&gt;ShowWindow <br />Before pMainFrame-&gt;UpdateWindow <br />  <br />// 退出时的 TRACE <br />CMDITestView::~CMDITestView() <br />CChildFrame::~CChildFrame() <br />CMDITestDoc::~CMDITestDoc() <br />CMainFrame::~CMainFrame() <br /><img src ="http://www.cppblog.com/killsound/aggbug/20375.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/killsound/" target="_blank">小不懂^_^</a> 2007-03-22 19:34 <a href="http://www.cppblog.com/killsound/articles/20375.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>