📄 wtl for mfc programmers, part i - atl gui classes - wtl.htm
字号:
<html>
<head>
<title>WTL for MFC Programmers, Part I - ATL GUI Classes</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
</head>
<body bgcolor="#33CCCC" text="#000000">
<p align="center"><b><font style="FONT-SIZE: 16pt" size="4" color="#0033CC">WTL
for MFC Programmers, Part I - ATL GUI Classes</font></b><br>
<br>
</p>
<p align="left">原作 :<b><font color="#CC3366">Michael Dunn</font></b> [<a href="http://www.codeproject.com/wtl/WTL4MFC1.asp">英文原文</a>]<br>
翻译 :<a href="mailto:inte2000@163.com">Orbit(桔皮干了)</a> [<a href="http://www.winmsg.com/cn/orbit.htm">http://www.winmsg.com/cn/orbit.htm</a>]</p>
<p align="left"><a href="demo/WTL4MFC1_demo.zip">下载演示程序代码</a></p>
<H2><font color="#FFFF66">本章内容</font></H2>
<UL>
<LI><A
href="#readme">README.TXT</A>
<LI><A
href="#seriesintro">对本系列文章的总体介绍</A>
<LI><A
href="#intopart1">对第一章的简单介绍</A>
<LI><A
href="#atlbackground">ATL 背景知识</A>
<UL>
<LI><A
href="#atlhistory">ATL 和
WTL的发展历史</A>
<LI><A
href="#atltemplates">ATL-style
模板</A> </LI>
</UL>
<LI><A
href="#atlwindowing">ATL 窗口类</A>
<LI><A
href="#windowimpl">定义一个窗口的实现</A>
<UL>
<LI><A
href="#msgmap">填写消息映射链(message
map)</A> </LI>
</UL>
<LI><A
href="#advmsgmap">高级消息映射链和嵌入类(Mix-in
Classes)</A>
<LI><A
href="#exestructure">ATL程序的结构</A>
<LI><A
href="#dialogs">ATL中的对话框</A>
<LI><A
href="#whereswtlman">我会继续讲WTL, 我保证!</A>
<LI><A
href="#revisionhistory">修改记录</A>
</LI>
</UL>
<H2><A name=readme></A><font color="#FFFF66">README.TXT</font></H2>
<P>在你开始使用WTL或着在本文章的讨论区张贴消息之前,我想请你先阅读下面的材料。</P>
<P>你需要开发平台SDK(Platform SDK)。你要使用WTL不能没有它,你可以使用<a href="http://www.microsoft.com/msdownload/platformsdk/sdkupdate/">在线升级</a>安装开发平台SDK,也可以<a href="http://www.microsoft.com/msdownload/platformsdk/sdkupdate/psdk-full.htm">下载全部文件</a>后在本地安装。在使用之前要将SDK的包含文件(.h头文件)和库文件(.Lib文件)路径添加到VC的搜索目录,SDK有现成的工具完成这个工作,这个工具位于开发平台SDK程序组的“<i>Visual
Studio Registration</i>”文件夹里。</P>
<P>你需要安装 WTL。你可以从微软的网站上<a href="http://download.microsoft.com/download/VisualStudioNET/Install/7.0/WXP/EN-US/WTL70.exe">下载WTL的7.0版</a>,在安装之前可以先查看“<a
href="http://www.codeproject.com/wtl/wtlintro1.asp">Introduction to WTL
- Part 1</a>”和“<a href="http://www.codeproject.com/wtl/wtlinst.asp">Easy installation
of WTL</a>”这两篇文章,了解一下所要安装的文件的信息,虽然现在这些文章有些过时,但还是可以提供很多有用的信息。有一件我认为不该在本篇文章中提到的事是告诉VC如何搜索WTL的包含文件路径,如果你用的VC6,用鼠标点击
<i>Tools\Options</i>,转到<i>Directories</i>标签页,在显示路径的列表框中选择<i>Include Files</i>,然后将WTL的包含文件的存放路径添加到包含文件搜索路径列表中。</P>
<P>你需要了解MFC。很好地了解MFC将有助于你理解后面提到的有关消息映射的宏并能够编辑那些标有“不要编辑(DO NOT EDIT)”的代码而不会出现问题。</P>
<P>你需要清楚地知道如何使用Win32 API编程。如果你是直接从MFC开始学习Windows编程,没有学过API级别的消息处理方式,那很不幸你会在使用WTL时遇到麻烦。如果不了解Windows消息中WPARAM参数和LPARAM参数的意义,应该明白需要读一些这方面的文章(在CodeProject有大量的此类文章)。</P>
<P>你需要知道 C++ 模板的语法,你可以到<A
href="http://www.codeproject.com/cpp/cppforumfaq.asp#other_cpp">VC Forum
FAQ</A> 相关的连接寻求答案。</P>
<P>我只是讨论了一些涵盖VC 6的特点,不过据我了解所有的程序都可以在VC 7上使用。由于我不使用VC 7,我无法对那些在VC 7中出现的问题提供帮助,不过你还是可以放心的在此张贴你的问题,因为其他的人可能会帮助你。</P>
<h2><a name=seriesintro></a><font color="#FFFF66">对本系列文章的总体介绍</font></h2>
<P>WTL 具有两面性,确实是这样的。它没有MFC的界面(GUI)类库那样功能强大,但是能够生成很小的可执行文件。如果你象我一样使用MFC进行界面编程,你会觉得MFC提供的界面控件封装使用起来非常舒服,更不用说MFC内置的消息处理机制。当然,如果你也象我一样不希望自己的程序仅仅因为使用了MFC的框架就增加几百K的大小的话,WTL就是你的选择。当然,我们还要克服一些障碍:</P>
<UL>
<LI>ATL样式的模板类初看起来有点怪异
<LI>没有类向导的支持,所以要手工处理所有的消息映射。
<LI>MSDN没有正式的文档支持,你需要到处去收集有关的文档,甚至是查看WTL的源代码。
<LI>买不到参考书籍
<LI>没有微软的官方支持
<LI>ATL/WTL的窗口与MFC的窗口有很大的不同,你所了解的有关MFC的知识并不全部适用与WTL。</LI>
</UL>
<P>从另一方面讲,WTL也有它自身的优势:</P>
<UL>
<LI>不需要学习或掌握复杂的文档/视图框架。
<LI>具有MFC的基本的界面特色,比如DDX/DDV和命令状态的自动更新功能(译者加:比如菜单的Check标记和Enable标记)。
<LI>增强了一些MFC的特性(比如更加易用的分隔窗口)。
<LI>可生成比静态链接的MFC程序更小的可执行文件(译者加:WTL的所有源代码都是静态链接到你的程序中的)。
<LI>你可以修正自己使用的WTL中的错误(BUG)而不会影响其他的应用程序(相比之下,如果你修正了有BUG的MFC/CRT动态库就可能会引起其它应用程序的崩溃。
<br>
<LI>如果你仍然需要使用MFC,MFC的窗口和ATL/WTL的窗口可以“和平共处”。(例如我工作中的一个原型就使用了了MFC的<font color="#CC0066">CFrameWnd</font>,并在其内包含了WTL的<font color="#CC0000">CSplitterWindow</font>,在<font color="#CC0000">CSplitterWindow</font>中又使用了MFC的<font color="#CC0000">CDialogs</font>
-- 我并不是为了炫耀什么,只是修改了MFC的代码使之能够使用WTL的分割窗口,它比MFC的分割窗口好的多)。 <br>
</LI>
</UL>
<P>在这一系列文章中,我将首先介绍ATL的窗口类,毕竟WTL是构建与ATL之上的一系列附加类,所以需要很好的了解ATL的窗口类。介绍完ATL之后我将介绍WTL的特性以并展示它是如何使界面编程变得轻而易举。</P>
<H2><A name=intopart1></A><font color="#FFFF66">对第一章的简单介绍</font></H2>
<P>WTL是个很酷的工具,在理解这一点之前需要首先介绍ATL。WTL是构建与ATL之上的一系列附加类,如果你是个严格使用MFC的程序员那么你可能没有机会接触到ATL的界面类,所以请容忍我在开始WTL之前先罗索一些别的东西,绕道来介绍一下ATL是很有必要地。</P>
<P>在本文的第一部分,我将给出一点ATL的背景知识,包括一些编写ATL代码必须知道的基本知识,快速的解释一些令人不知所措的ATL模板类和基本的ATL窗口类。</P>
<H2><A name=atlbackground></A><font color="#FFFF66">ATL 背景知识</font></H2>
<H3><A name=atlhistory></A><font color="#FFFF66">ATL 和 WTL 的发展历史</font></H3>
<P>“活动模板库”(Active Template Library)是一个很古怪的名字,不是吗?那些年纪大的人可能还记得它最初被称为“网络组件模板库”,这可能是它更准确的称呼,因为ATL的目的就是使编写组件对象和ActiveX控件更容易一些(ATL是在微软开发新产品ActiveX-某某的过程中开发的,那些ActiveX-某某现在被称为某某.NET)。由于ATL是为了便于编写组件对象而存在的,所以只提供了简单的界面类,相当于MFC的窗口类(<font color="#CC0000">CWnd</font>)和对话框类(<font color="#CC0000">CDialog</font>)。幸运的是这些类非常的灵活,能够在其基础上构建象WTL这样的附加类。</P>
<P>WTL现在已经是第二次修正了,最初的版本是3.1,现在的版本是7(WTL的版本号之所以这样选择是为了与ATL的版本匹配,所以不存在1和2这样的版本号)。WTL
3.1可以与VC 6和VC 7一起使用,但是在VC 7下需要定义几个预处理标号。WTL 7向下兼容WTL 3.1,并且不作任何修改就可以与VC 7一起使用,现在看来没有任何理由还使用3.1来进行新的开发工作。<br>
</P>
<H3><A name=atltemplates></A><font color="#FFFF66">ATL-style 模板</font></H3>
<P>即使你能够毫不费力地阅读C++的模板类代码,仍然有两件事可能会使你有些头晕,以下面这个类的定义为例:</P>
<pre><SPAN class=cpp-keyword><font color="#3300FF">class</font></SPAN><font color="#3300FF"> CMyWnd : <SPAN class=cpp-keyword>public</SPAN> CWindowImpl<CMyWnd>
{
...
};</font> </pre>
<P>这样作是合法的,因为C++的语法解释说即使CMyWnd类只是被部分定义,类名CMyWnd已经被列入递归继承列表,是可以使用的。将类名作为模板类的参数是因为ATL要做另一件诡秘的事情,那就是编译期间的虚函数调用机制。</P>
<P>如果你想要了解它是如何工作地,请看下面的例子:</P>
<PRE><SPAN class=cpp-keyword><font color="#0000FF">template</font></SPAN> <font color="#0000FF"><<SPAN class=cpp-keyword>class</SPAN> T>
<SPAN class=cpp-keyword>class</SPAN> B1
{
<SPAN class=cpp-keyword>public</SPAN>:
<SPAN class=cpp-keyword>void</SPAN> SayHi()
{</font>
<FONT color=red> T* pT = <SPAN class=cpp-keyword>static_cast</SPAN><T*>(<SPAN class=cpp-keyword>this</SPAN>);</FONT> <SPAN class=cpp-comment>// HUH?? 我将在下面解释</SPAN>
<font color="#0000FF"> pT->PrintClassName();
}
<SPAN class=cpp-keyword>protected</SPAN>:
<SPAN class=cpp-keyword>void</SPAN> PrintClassName() { cout << <SPAN class=cpp-string>"This is B1"</SPAN>; }
};
<SPAN class=cpp-keyword>class</SPAN> D1 : <SPAN class=cpp-keyword>public</SPAN> B1<D1>
{
<SPAN class=cpp-comment>// No overridden functions at all</SPAN>
};
<SPAN class=cpp-keyword>class</SPAN> D2 : <SPAN class=cpp-keyword>public</SPAN> B1<D2>
{
<SPAN class=cpp-keyword>protected</SPAN>:
<SPAN class=cpp-keyword>void</SPAN> PrintClassName() { cout << <SPAN class=cpp-string>"This is D2"</SPAN>; }
};
main()
{
D1 d1;
D2 d2;
d1.SayHi(); <SPAN class=cpp-comment>// prints "This is B1"</SPAN>
d2.SayHi(); <SPAN class=cpp-comment>// prints "This is D2"</SPAN>
}</font></PRE>
<P>这句代码static_cast<T*>(this) 就是窍门所在。它根据函数调用时的特殊处理将指向B1类型的指针this指派为D1或D2类型的指针,因为模板代码是在编译其间生成的,所以只要编译器生成正确的继承列表,这样指派就是安全的。(如果你写成:</P>
<PRE><SPAN class=cpp-keyword><font color="#0000FF">class</font></SPAN><font color="#0000FF"> D3 : <SPAN class=cpp-keyword>public</SPAN> B1<D2></font></PRE>
<P>就会有麻烦) 之所以安全是因为this对象只可能是指向D1或D2(在某些情况下)类型的对象,不会是其他的东西。注意这很像C++的多态性(polymorphism),只是SayHi()方法不是虚函数。</P>
<P>要解释这是如何工作的,首先看对每个SayHi()函数的调用,在第一个函数调用,对象B1被指派为D1,所以代码被解释成:</P>
<PRE><SPAN class=cpp-keyword><font color="#0000FF">void</font></SPAN><font color="#0000FF"> B1<D1>::SayHi()
{
D1* pT = <SPAN class=cpp-keyword>static_cast</SPAN><D1*>(<SPAN class=cpp-keyword>this</SPAN>);
pT->PrintClassName();
}</font></PRE>
<P>由于D1没有重载PrintClassName(),所以查看基类B1,B1有PrintClassName(),所以B1的PrintClassName()被调用。</P>
<P>现在看第二个函数调用SayHi(),这一次对象被指派为D2类型,SayHi()被解释成:</P>
<PRE><SPAN class=cpp-keyword><font color="#0000FF">void</font></SPAN><font color="#0000FF"> B1<D2>::SayHi()
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -