WTL教程Word文件下载.docx
- 文档编号:22272300
- 上传时间:2023-02-03
- 格式:DOCX
- 页数:128
- 大小:99.40KB
WTL教程Word文件下载.docx
《WTL教程Word文件下载.docx》由会员分享,可在线阅读,更多相关《WTL教程Word文件下载.docx(128页珍藏版)》请在冰豆网上搜索。
它没有MFC的界面(GUI)类库那样功能强大,但是能够生成很小的可执行文件。
如果你象我一样使用MFC进行界面编程,你会觉得MFC提供的界面控件封装使用起来非常舒服,更不用说MFC内置的消息处理机制。
当然,如果你也象我一样不希望自己的程序仅仅因为使用了MFC的框架就增加几百K的大小的话,WTL就是你的选择。
当然,我们还要克服一些障碍:
ATL样式的模板类初看起来有点怪异,
没有类向导的支持,所以要手工处理所有的消息映射。
MSDN没有正式的文档支持,你需要到处去收集有关的文档,甚至是查看WTL的源代码。
买不到参考书籍
没有微软的官方支持
ATL/WTL的窗口与MFC的窗口有很大的不同,你所了解的有关MFC的知识并不全部适用与WTL。
从另一方面讲,WTL也有它自身的优势:
不需要学习或掌握复杂的文档/视图框架。
具有MFC的基本的界面特色,比如DDX/DDV和命令状态的自动更新功能(译者加:
比如菜单的Check标记和Enable标记)。
增强了一些MFC的特性(比如更加易用的分隔窗口)。
可生成比静态链接的MFC程序更小的可执行文件(译者加:
WTL的所有源代码都是静态链接到
你的程序中的)。
你可以修正自己使用的WTL中的错误(BUG)而不会影响其他的应用程序(相比之下,如果你修正了有BUG的MFC/CRT动态库就可能会引起其它应用程序的崩溃。
如果你仍然需要使用MFC,MFC的窗口和ATL/WTL的窗口可以“和平共处”。
(例如我工作中的一个原型就使用了了MFC的CFrameWnd,并在其内包含了WTL的CSplitterWindow,在CSplitterWindow中又使用了MFC的CDialogs--我并不是为了炫耀什么,只是修改了MFC的代码使之能够使用WTL的分割窗口,它比MFC的分割窗口好的多)。
在这一系列文章中,我将首先介绍ATL的窗口类,毕竟WTL是构建与ATL之上的一系列附加类,所以需要很好的了解ATL的窗口类。
介绍完ATL之后我将介绍WTL的特性以并展示它是如何使界面编程变得轻而易举。
第一部分ATL中的GUI类
对第一章的简单介绍
WTL是个很酷的工具,在理解这一点之前需要首先介绍ATL。
WTL是构建与ATL之上的一系列附加类,如果你是个严格使用MFC的程序员那么你可能没有机会接触到ATL的界面类,所以请容忍我在开始WTL之前先罗索一些别的东西,绕道来介绍一下ATL是很有必要地。
在本文的第一部分,我将给出一点ATL的背景知识,包括一些编写ATL代码必须知道的基本知识,快速的解释一些令人不知所措的ATL模板类和基本的ATL窗口类。
ATL背景知识
ATL和WTL的发展历史
“活动模板库”(ActiveTemplateLibrary)是一个很古怪的名字,不是吗?
那些年纪大的人可能还记得它最初被称为“网络组件模板库”,这可能是它更准确的称呼,因为ATL的目的就是使编写组件对象和ActiveX控件更容易一些(ATL是在微软开发新产品ActiveX-某某的过程中开发的,那些ActiveX-某某现在被称为某某.NET)。
由于ATL是为了便于编写组件对象而存在的,所以只提供了简单的界面类,相当于MFC的窗口类(CWnd)和对话框类(CDialog)。
幸运的是这些类非常的灵活,能够在其基础上构建象WTL这样的附加类。
WTL现在已经是第二次修正了,最初的版本是3.1,现在的版本是7(WTL的版本号之所以这样选择是为了与ATL的版本匹配,所以不存在1和2这样的版本号)。
WTL3.1可以与VC6和VC7一起使用,但是在VC7下需要定义几个预处理标号。
WTL7向下兼容WTL3.1,并且不作任何修改就可以与VC7一起使用,现在看来没有任何理由还使用3.1来进行新的开发工作。
ATL-style模板
即使你能够毫不费力地阅读C++的模板类代码,仍然有两件事可能会使你有些头晕,以下面这个类的定义为例:
classCMyWnd:
publicCWindowImpl<
CMyWnd>
{
...
};
这样作是合法的,因为C++的语法解释说即使CMyWnd类只是被部分定义,类名CMyWnd已经被列入递归继承列表,是可以使用的。
将类名作为模板类的参数是因为ATL要做另一件诡秘的事情,那就是编译期间的虚函数调用机制。
如果你想要了解它是如何工作地,请看下面的例子:
template<
classT>
classB1
public:
voidSayHi()
T*pT=static_cast<
T*>
(this);
//HUH?
?
我将在下面解释
pT->
PrintClassName();
}
protected:
voidPrintClassName(){cout<
<
"
ThisisB1"
;
}
classD1:
publicB1<
D1>
//Nooverriddenfunctionsatall
classD2:
D2>
ThisisD2"
main()
D1d1;
D2d2;
d1.SayHi();
//prints"
d2.SayHi();
这句代码static_cast<
(this)就是窍门所在。
它根据函数调用时的特殊处理将指向B1类型的指针this指派为D1或D2类型的指针,因为模板代码是在编译其间生成的,所以只要编译器生成正确的继承列表,这样指派就是安全的。
(如果你写成:
classD3:
就会有麻烦)之所以安全是因为this对象只可能是指向D1或D2(在某些情况下)类型的对象,不会是其他的东西。
注意这很像C++的多态性(polymorphism),只是SayHi()方法不是虚函数。
要解释这是如何工作的,首先看对每个SayHi()函数的调用,在第一个函数调用,对象B1被指派为D1,所以代码被解释成:
voidB1<
:
SayHi()
D1*pT=static_cast<
D1*>
由于D1没有重载PrintClassName(),所以查看基类B1,B1有PrintClassName(),所以B1的PrintClassName()被调用。
现在看第二个函数调用SayHi(),这一次对象被指派为D2类型,SayHi()被解释成:
D2*pT=static_cast<
D2*>
这一次,D2含有PrintClassName()方法,所以D2的PrintClassName()方法被调用。
这种技术的有利之处在于:
不需要使用指向对象的指针。
节省内存,因为不需要虚函数表。
因为没有虚函数表所以不会发生在运行时调用空指针指向的虚函数。
所有的函数调用在编译时确定(译者加:
区别于C++的虚函数机制使用的动态编连),有利于编译程序对代码的优化。
节省虚函数表在这个例子中看起来无足轻重(每个虚函数只有4个字节),但是设想一下如果有15个基类,每个类含有20个方法,加起来就相当可观了。
ATL窗口类
好了,关于ATL的背景知识已经讲的构多了,到了该正式讲ATL的时候了。
ATL在设计时接口定义和实现是严格区分开的,这在窗口类的设计中是最明显的,这一点类似于COM,COM的接口定义和实现是完全分开的(或者可能有多个实现)。
ATL有一个专门为窗口设计的接口,可以做全部的窗口操作,这就是CWindow。
它实际上就是对HWND操作的包装类,对几乎所有以HWND句柄为第一个参数的窗口API的进行了封装,例如:
SetWindowText()和DestroyWindow()。
CWindow类有一个公有成员m_hWnd,使你可以直接对窗口的句柄操作,CWindow还有一个操作符HWND,你可以讲CWindow对象传递给以HWND为参数的函数,但这与CWnd:
GetSafeHwnd()(译者加:
MFC的方法)没有任何等同之处。
CWindow与MFC的CWnd类有很大的不同,创建一个CWindow对象占用很少的资源,因为只有一个数据成员,没有MFC窗口中的对象链,MFC内部维持这一个对象链,此对象链将HWND映射到CWnd对象。
还有一点与MFC的CWnd类不同的是当一个CWindow对象超出了作用域,它关联的窗口并不被销毁掉,这意味着你并不需要随时记得分离你所创建的临时CWindow对象。
在ATL类中对窗口过程的实现是CWindowImpl。
CWindowImpl含有所有窗口实现代码,例如:
窗口类的注册,窗口的子类化,消息映射以及基本的WindowProc()函数,可以看出这与MFC的设计有很大的不同,MFC将所有的代码都放在一个CWnd类中。
还有两个独立的类包含对话框的实现,它们分别是CDialogImpl和CAxDialogImpl,CDialogImpl用于实现普通的对话框而CAxDialogImpl实现含有ActiveX控件的对话框。
定义一个窗口的实现
任何非对话框窗口都是从CWindowImpl派生的,你的新类需要包含三件事情:
一个窗口类的定义
一个消息映射链
窗口使用的默认窗口类型,称为windowtraits
窗口类的定义通过DECLARE_WND_CLASS宏或DECLARE_WND_CLASS_EX宏来实现。
这辆个宏定义了一个CWndClassInfo结构,这个结构封装了WNDCLASSEX结构。
DECLARE_WND_CLASS宏让你指定窗口类的类名,其他参数使用默认设置,而DECLARE_WND_CLASS_EX宏还允许你指定窗口类的类型和窗口的背景颜色,你也可以用NULL作为类名,ATL会自动为你生成一个类名。
让我们开始定义一个新类,在后面的章节我会逐步的完成这个类的定义。
classCMyWindow:
CMyWindow>
DECLARE_WND_CLASS(_T("
MyWindowClass"
))
接下来是消息映射链,ATL的消息映射链比MFC的简单的多,ATL的消息映射链被展开为switch语句,switch语句正确的消息处理者并调用相应的函数。
使用消息映射链的宏是BEGIN_MSG_MAP和END_MSG_MAP,让我们为我们的窗口添加一个空的消息映射链。
BEGIN_MSG_MAP(CMyWindow)
END_MSG_MAP()
我将在下一节展开讲如何如何添加消息处理到消息映射链。
最后,我们需要为我们的窗口类定义窗口的特征,窗口的特征就是窗口类型和扩展窗口类型的联合体,用于创建窗口时指定窗口的类型。
窗口类型被指定为参数模板,所以窗口的调用者不需要为指定窗口的正确类型而烦心,下面是是同ATL类CWinTraits定义窗口类型的例子:
typedefCWinTraits<
WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN,WS_EX_APPWINDOW>
CMyWindowTraits;
CMyWindow,CWindow,CMyWindowTraits>
调用者可以重载CMyWindowTraits的类型定义,但是一般情况下这是没有必要的,ATL提供了几个预先定义的特殊的类型,其中之一就是CFrameWinTraits,一个非常棒的框架窗口:
WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN|WS_CLIPSIBLINGS,WS_EX_APPWINDOW|WS_EX_WINDOWEDGE>
CFrameWinTraits;
填写消息映射链
ATL的消息映射链是对开发者不太友好的部分,也是WTL对其改进最大的部分。
类向导至少可以让你添加消息响应,然而ATL没有消息相关的宏和象MFC那样的参数自动展开功能,在ATL中只有三种类型的消息处理,一个是WM_NOTIFY,一个是WM_COMMAND,第三类是其他窗口消息,让我们开始为我们的窗口添加WM_CLOSE和WM_DESTROY的消息相应函数。
CMyWindow,CWindow,CFrameWinTraits>
MESSAGE_HANDLER(WM_CLOSE,OnClose)
MESSAGE_HANDLER(WM_DESTROY,OnDestroy)
LRESULTOnClose(UINTuMsg,WPARAMwParam,LPARAMlParam,BOOL&
bHandled)
DestroyWindow();
return0;
LRESULTOnDestroy(UINTuMsg,WPARAMwParam,LPARAMlParam,BOOL&
PostQuitMessage(0);
你可能注意到消息响应函数的到的是原始的WPARAM和LPARAM值,你需要自己将其展开为相应的消息所需要的参数。
还有第四个参数bHandled,这个参数在消息相应函数调用被ATL设置为TRUE,如果在你的消息响应处理完之后需要ATL调用默认的WindowProc()处理该消息,你可以讲bHandled设置为FALSE。
这与MFC不同,MFC是显示的调用基类的响应函数来实现的默认的消息处理的。
让我们也添加一个对WM_COMMAND消息的处理,假设我们的窗口有一个ID为IDC_ABOUT的About菜单:
COMMAND_ID_HANDLER(IDC_ABOUT,OnAbout)
LRESULTOnAbout(WORDwNotifyCode,WORDwID,HWNDhWndCtl,BOOL&
MessageBox(_T("
SampleATLwindow"
),_T("
AboutMyWindow"
));
需要注意得是COMMAND_HANDLER宏已经将消息的参数展开了,同样,NOTIFY_HANDLER宏也将WM_NOTIFY消息的参数展开了。
高级消息映射链和嵌入类
ATL的另一个显著不同之处就是任何一个C++类都可以响应消息,而MFC只是将消息响应任务分给了CWnd类和CCmdTarget类,外加几个有PreTranslateMessage()方法的类。
ATL的这种特性允许我们编写所谓的“嵌入类”,为我们的窗口添加特性只需将该类添加到继承列表中就行了,就这么简单!
一个基本的带有消息映射链的类通常是模板类,将派生类的类名作为模板的参数,这样它就可以访问派生类中的成员,比如m_hWnd(CWindow类中的HWND成员)。
让我们来看一个嵌入类的例子,这个嵌入类通过响应WM_ERASEBKGND消息来画窗口的背景。
classT,COLORREFt_crBrushColor>
classCPaintBkgnd:
publicCMessageMap
CPaintBkgnd(){m_hbrBkgnd=CreateSolidBrush(t_crBrushColor);
~CPaintBkgnd(){DeleteObject(m_hbrBkgnd);
BEGIN_MSG_MAP(CPaintBkgnd)
MESSAGE_HANDLER(WM_ERASEBKGND,OnEraseBkgnd)
LRESULTOnEraseBkgnd(UINTuMsg,WPARAMwParam,LPARAMlParam,BOOL&
HDCdc=(HDC)wParam;
RECTrcClient;
GetClientRect(&
rcClient);
FillRect(dc,&
rcClient,m_hbrBkgnd);
return1;
//wepaintedthebackground
HBRUSHm_hbrBkgnd;
让我们来研究一下这个新类。
首先,CPaintBkgnd有两个模板参数:
使用CPaintBkgnd的派生类的名字和用来画窗口背景的颜色。
(t_前缀通常用来作为模板类的模板参数的前缀)CPaintBkgnd也是从CMessageMap派生的,这并不是必须的,因为所有需要响应消息的类只需使用BEGIN_MSG_MAP宏就足够了,所以你可能看到其他的一些嵌入类的例子代码,它们并不是从该基类派生的。
构造函数和析构函数都相当简单,只是创建和销毁Windows画刷,这个画刷由参数t_crBrushColor决定颜色。
接着是消息映射链,它响应WM_ERASEBKGND消息,最后由响应函数OnEraseBkgnd()用构造函数创建的画刷填充窗口的背景。
在OnEraseBkgnd()中有两件事需要注意,一个是它使用了一个派生的窗口类的方法(即GetClientRect()),我们如何知道派生类中有GetClientRect()方法呢?
如果派生类中没有这个方法我们的代码也不会有任何抱怨,由编译器确认派生类T是从CWindow派生的。
另一个是OnEraseBkgnd()没有将消息参数wParam展开为设备上下文(DC)。
(WTL最终会解决这个问题,我们很快就可以看到,我保证)
要在我们的窗口中使用这个嵌入类需要做两件事:
首先,将它加入到继承列表:
publicCPaintBkgnd<
CMyWindow,RGB(0,0,255)>
其次,需要CMyWindow将消息传递给CPaintBkgnd,就是将其链入到消息映射链,在CMyWindow的消息映射链中加入CHAIN_MSG_MAP宏:
CMyWindow,CWind
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- WTL 教程