您好,欢迎光临本网站![请登录][注册会员]  
文件名称: WTL中文版CHM格式
  所属分类: C++
  开发工具:
  文件大小: 2mb
  下载次数: 0
  上传时间: 2009-07-18
  提 供 者: son***
 详细说明: 第一部分 - ATL 中的 GUI 类 第二部分 - WTL 中的 GUI 基础类 第三部分 - 工具栏和状态栏 第四部分 - 对话框和控件 第五部分 - 高级对话框 UI 类 第六部分 - 掌控 ActiveX 控件 第七部分 - 分割条窗口 第八部分 - 属性表和向导 第九部分 - GDI 类,公用对话框以及辅助类 本章内容 README.TXT 本系列介绍 第一部分介绍 ATL 背景知识 ATL 和 WTL 的历史 ATL 风格的模板 ATL 窗口类 定义窗口实现 填充消息映射 高级消息映射 链和嵌入(Mix-in)类 ATL EXE 的结构 VC 6 的情形 VC 7 的情形 ATL 中的对话框 就要到 WTL 了,我保证! 修订历史 README.TXT 在继续或者在本文的讨论板块中发布帖子之前,我希望你能先阅读以下内容。 本系列原来是为 VC 6 用户写的,介绍 WTL 7.0 的内容。现在 VC 8 已经出来了,我觉得也到了更新本系列来介绍 VC 7.1 的时候了。;)(不过,VC 7.1 的从 6 到 7 的自动转换工作并不是总能平滑地完成,所以 VC 7.1 的用户在试着使用示例源代码的时候可能会遭遇失败)因而,我将继续下去,持续更新本系列。文章将更新到可以反映 WTL 7.1 的特性,并会在下载的源代码中包括 VC 7.1 的工程。 针对 VC 2005 用户的重要提示:VC 2005 的 Express 版本并不附带 ATL 或者 MFC,因此不能使用此版本编译 ATL 或者 WTL 工程。 如果你在使用 VC 6,那你就需要有 Platform SDK。没有它你将不能使用 WTL。你可以使用Web 安装版本或者下载 CAB 文件或者是 ISO 映像,然后在本地运行安装程序。请使用工具把 SDK 的 include 以及 lib 目录加入到 VC 的搜索路径中,该工具可以在 Platform SDK 程序组中的 Visual Studio Registration 文件夹下找到。即使你在用 VC 7,使用最新的 Platform SDK 仍然是一个好主意,因为你可以得到最新的头文件和库。 你需要有 WTL。可以从微软下载版本 7。在文章 "Introduction to WTL - Part 1" 以及 "Easy installation of WTL" 中有一些关于安装的提示。这些文章已经很老了,不过还是有一些不错的信息。WTL 分发包里也有一个 readme 文件,里面有安装指令。我认为在这些文章中没有提到的一件事情是如何把 WTL 的文件加入到 VC 的包含路径里。在 VC 6 里,点击 Tools|Options 并切换到 Directories 标签页,在 Show directories for 组合框中,选中 Include files,然后添加一个新项,使其指向你放置 WTL 头文件的目录。在 VC 7 里,点击 Tools|Options,再点击 Projects,然后是 VC++ Directories,在 Show directories for 组合框中,选中 Include files,然后添加一个新项,使其指向你放置 WTL 头文件的目录。 重要:我们正在提及 VC 7 的包含路径这一话题,如果你还没有更新 Platform SDK,你必须对缺省的目录列表做一个改动。请确保 $(VCInstallDir)PlatformSDK\include 在列表的第一位,优先于 $(VCInstallDir)include,如下图所示: 你应该了解 MFC,并且要了解到你知道消息映射宏的实质是什么,而且能够编辑那些被标记为“DO NOT EDIT”的代码而不出问题。 你需要了解 Win32 API 编程,而且是很好地了解。如果你是直接通过 MFC 学习 Windows 编程而没有学习在 API 级消息是如何工作的,那很不幸,你会在使用 WTL 时遇到麻烦。如果你不知道一个消息的 WPARAM 和 LPARAM 是什么意思,你应该阅读其他的 API 级编程的文章(CodeProject 上就有很多)以使你能够了解。 你需要了解 C++ 模板的语法,在 VC Forum FAQ 上有 C++ FAQ 和模板 FAQ 的链接。 因为我还没有使用 VC 8,所以我不知道示例代码在 8 上是不是可以编译,希望 7 到 8 的升级过程能比 6 到 7 的强。如果在 VC 8 上有任何问题,请张贴到本文的论坛里。 本系列介绍 WTL 确实震动了所有人。它具有许多 MFC GUI 类的强大功能,但是可以生成相当小的可执行代码。如果你和我一样,用 MFC 学习 GUI 编程,对 MFC 所提供的控件封装感到相当舒服,并且对 MFC 内建的灵活的消息处理也有同感;如果你和我一样,不喜欢好几百 K 的 MFC 框架附着到自己的程序上,WTL 正适合你。 不过,还是有一些我们必须跨越的障碍: ATL 风格的模板乍看起来很怪异。 没有 ClassWizard 支持,所以写消息映射成了手工劳动。 在 MSDN 里没有文档,需要到其他地方去找,甚至需要去看 WTL 源程序。 没有能买到并放到书架上的参考书。 它具有“不被微软官方支持”的污名 ATL/WTL 窗口非常不同于 MFC 窗口,并非你所有的知识都能够对应过来 另一方面,WTL 的好处有: 不需学习或者使用复杂的文档/视图框架 具有源于 MFC 的一些基本 UI 特性,例如 DDX/DDV 和“更新命令 UI”功能 增强了的一些 MFC 特性(例如,更灵活的分割条窗口) 与静态链接 MFC 的应用相比,可执行代码非常小 你自己可以改正 WTL 的错误而不影响现存的应用(相比之下,一个应用替换掉 MFC/CRT 的 DLL 来改正错误将引起其他应用崩溃) 如果仍然需要 MFC,MFC 和 ATL/WTL 窗口可以和平共处(在我工作的一个原型中,我创建了一个包含有 WTL CSplitterWindow 的 MFC CFrameWnd ,而前者中又包含有 MFC CDialog。-- 并不是我卖弄,只不过是修改了 MFC 代码而使用了更好的 WTL 分割条) 在本系列中,我将先介绍 ATL 窗口类。毕竟 WTL 是一组 ATL 的附加类,所以对 ATL 窗口有很好的理解相当重要。介绍完 ATL 之后我将介绍 WTL 的特性并展示它如何使界面编程变得轻而易举。 第一部分介绍 WTL 令人震惊。不过在知道为什么之前,我们首先需要了解 ATL。WTL 是一组 ATL 的附加类,如果过去你是一名仅使用 MFC 的程序员,你可能从来没有遇到过 ATL 的 GUI 类。所以请原谅我没有立即涉及 WTL,到 ATL 那儿绕些弯路是有必要的。 在第一部分里,我将给出一些 ATL 的背景知识,包括在写 ATL 代码之前需要知道的一些要点,迅速的解释那些令人胆寒的 ATL 模板,并涵盖了基本的 ATL 窗口类。 ATL 背景知识 ATL 和 WTL 的历史 活动(Active)模板库是一个古怪的名字,不是吗?年长点的可能会记得它原来的名字是 ActiveX 模板库,这是一个更准确的名字,因为 ATL 的目标就是要让 COM 对象和 ActiveX 控件写起来更轻松。(ATL 是在微软将新产品命名为“ActiveX-什么什么”的时候开发的,就像现在微软的新产品被称作“什么什么 .NET”一样)因为 ATL 只是用来写 COM 对象的,所以它只有 GUI 类中最基本的部分,即 MFC 中 CWnd 和 CDialog 的等价物。幸运的是,这些 GUI 类很灵活,可以让像 WTL 这样的东西构筑于其上。 作为微软所有的一个项目,WTL 有两个大的修订版,3 和 7。(选定的版本号是为了与 ATL 的版本号匹配,所以不是 1 和 2。)版本 3.1 已经相当古老了,本系列中将不再涉及。版本 7 是版本 3 的一个重要升级,而版本 7.1 仅仅加入了一些纠错和少许的特性。 在版本 7.1 之后,微软将 WTL 作为了一个开源工程,托管于 Sourceforge 上。此站点上最新的版本是 7.5(译者注:目前已经是 8.0 了),我还没有看 7.5,所以现在本系列不会涵盖 7.5 的内容(我总是会落后两个版本,而且会周期性地赶上来!) ATL 风格的模板 即使你可以毫不头痛的阅读 C++ 模板,但一开始 ATL 还是会有两件事可能成为拦路虎。比如说:class CMyWnd : public CWindowImpl { ... }; 这样做是合法的,因为 C++ 规范中声称紧随 class CMyWnd 部分之后,名字 CMyWnd 即被定义;并且可以被用在继承列表中。之所以把类名作为模板参数是因为要让 ATL 能做第二件技巧性的工作 - 编译期虚函数调用。 如果要看一下实际运作,可以看一下这几个类:template class B1 { public: void SayHi() { T* pT = static_cast(this); // HUH?? I'll explain this below pT->PrintClassName(); } protected: void PrintClassName() { cout << "This is B1"; } }; class D1 : public B1 { // No overridden functions at all }; class D2 : public B1 { protected: void PrintClassName() { cout << "This is D2"; } }; main() { D1 d1; D2 d2; d1.SayHi(); // prints "This is B1" d2.SayHi(); // prints "This is D2" } 这儿的 static_cast(this) 是一个技巧。它把 B1* 类型的 this ,通过被调用的特化转型为 D1* 或者 D2* 。因为模板代码在编译时生成,所以保证了此转型是安全的,只要正确的书写了继承列表。 (如果你写成 class D3 : public B1 你就会遇到麻烦。)转型是安全的是因为 this 对象只能是类型 D1* 或者 D2* (相应的),而不是其他。注意,这和正常的 C++ 多态几乎一样,只不过 SayHi() 方法不是虚拟的。 要解释这是如何工作的,我们来看一下 SayHi() 的每个调用。在第一个调用中,特化 B1 被采用,所以 SayHi() 代码展开为:void B1::SayHi() { D1* pT = static_cast(this); pT->PrintClassName(); } 由于 D1 没有覆盖 PrintClassName(),所以会搜索 D1 的基类。B1 有 PrintClassName() 方法,所以就是被调到的那个。 现在,看 SayHi() 的第二次调用。这一次使用了特化 B1,于是 SayHi() 展开为:void B1::SayHi() { D2* pT = static_cast(this); pT->PrintClassName(); } 这次 D2 确实包含了一个 PrintClassName() 方法,所以它是被调用到的那个。 这种技术的好处是: 不需要使用指向对象的指针 由于不需要 vtbl 而节省内存 不会因为未初始化的 vtbl 而在运行时通过空指针调用虚函数 所有函数调用在编译时被解析,所以可以被优化 虽然在这个例子里 vtbl 的节约看起来并不明显(只不过 4 个字节),不过可以考虑在有 15 个基类,有的类有 20 个方法的情况下,累计的节省有多少。 ATL 窗口类 很好,背景知识足够了!是钻研 ATL 的时候了。ATL 采用严格的接口/实现相分离来设计,这在窗口类中也很明显。这和 COM 类似,接口的定义与实现完全分离(或者可能有多个实现)。 ATL 有一个类定义了窗口的“接口”,也就是对一个窗口可以做什么。这个类就是 CWindow。它只是对 HWND 的一个封装,提供了几乎所有的以 HWND 作为第一个参数的 User32 API,如 SetWindowText() 和 DestroyWindow()。 CWindow 有一个当你需要原始 HWND 时可以访问的公用成员 m_hWnd。CWindow 也有一个 operator HWND 方法,于是你可以传递 CWindow 对象到接受 HWND 的函数中。没有 CWnd::GetSafeHwnd()的等价物。 CWindow 和 MFC 的 CWnd 很不同。创建 CWindow 对象不需多少代价,因为它只有一个数据成员,而且也没有 MFC 内部用以保存 HWND 到 CWnd 对象的对应关系的对象映射表。还与 CWnd不一样的是,当一个 CWindow 对象离开作用域,关联的窗口不会被销毁。这意味着你不须总是记住要把你创建的临时 CWindow 对象与关联的窗口脱离。 ATL 中窗口的实现类是 CWindowImpl。CWindowImpl 包含了做这些事情的代码,比如窗口类注册,窗口子类化,消息映射,以及一个基本的 WindowProc()。再次不同于 MFC,在MFC 中,所有这些在同一个类里:CWnd。 还有两个独立的类,包含了对对话框的实现,CDialogImpl 和 CAxDialogImpl。CDialogImpl 用于普通的对话框,而 CAxDialogImpl 用于要掌控 ActiveX 控件的对话框。 定义窗口实现 任何要创建的非对话框窗口应该从 CWindowImpl 派生。新类里需要包含三样: 窗口类定义 消息映射 窗口使用的默认风格,称作窗口修饰 窗口类的定义使用 DECLARE_WND_CLASS 或者 DECLARE_WND_CLASS_EX 宏来完成。它们都定义了一个封装了 WNDCLASSEX 结构的 ATL 结构 CWndClassInfo。 DECLARE_WND_CLASS 允许指定新窗口类的名字,其余成员使用缺省值;而 DECLARE_WND_CLASS_EX 还允许指定类风格和窗口背景色。类名也可以用 NULL,ATL 将生成一个。 我们从一个新的类的定义开始,随后的章节里我会逐步向其中增加内容。class CMyWindow : public CWindowImpl { public: DECLARE_WND_CLASS(_T("My Window Class")) }; 接下来是消息映射。ATL 的消息映射比 MFC 的映射简单的多。一个 ATL 映射被展开为一个大的 switch 语句。switch 查找合适的处理器并调用相应的函数。消息映射的宏是 BEGIN_MSG_MAP 和 END_MSG_MAP 。向窗口中添加一个空的映射。class CMyWindow : public CWindowImpl { public: DECLARE_WND_CLASS(_T("My Window Class")) BEGIN_MSG_MAP(CMyWindow) END_MSG_MAP() }; 我会在下一节中讲解如何向映射中添加处理器。最后,我们要为我们的类定义窗口修饰。窗口修饰是窗口风格和窗口扩展风格的组合,这些风格在创建窗口时会被用到。这些风格作为模板参数被指定,所以调用者在创建窗口时可以不被如何得到正确的风格而烦恼。这是一个示例的修饰定义,使用了 ATL 类 CWinTraits:typedef CWinTraits CMyWindowTraits; class CMyWindow : public CWindowImpl { public: DECLARE_WND_CLASS(_T("My Window Class")) BEGIN_MSG_MAP(CMyWindow) END_MSG_MAP() }; 调用者可以在 CMyWindowTraits 定义中覆盖这些风格,不过通常没有必要。ATL 还有一些专用的预定义 CWinTraits ,其中适用于像我们这样的顶级窗口的一个是 CFrameWinTraits:typedef CWinTraits CFrameWinTraits; 填充消息映射 ATL 的消息映射对开发人员来讲是一个缺乏友好性的地方,同时也是 WTL 极大的增强了的地方。ClassView 提供了添加消息处理器的功能,但 ATL 没有类似于 MFC 的特定消息相关的宏和自动化参数解析。在 ATL 里,只有三种类型的消息处理器,一个针对 WM_NOTIFY,一个针对 WM_COMMAND,一个针对其他所有的消息。我们从向窗口添加 WM_CLOSE 和 WM_DESTROY 的消息处理器开始。class CMyWindow : public CWindowImpl { public: DECLARE_WND_CLASS(_T("My Window Class")) BEGIN_MSG_MAP(CMyWindow) MESSAGE_HANDLER(WM_CLOSE, OnClose) MESSAGE_HANDLER(WM_DESTROY, OnDestroy) END_MSG_MAP() LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { DestroyWindow(); return 0; } LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { PostQuitMessage(0); return 0; } }; 你会注意到处理器接收原始的 WPARAM 和 LPARAM 值,当消息使用这些参数时你需要自己去解析它们。还有第四个参数,bHandled。这个参数在处理期调用之前被 ATL 设为 TRUE。如果你希望 ATL 的缺省 WindowProc() 在你的处理器返回之后也能处理消息,你可以将 bHandled 设为 FALSE。这和 MFC 不一样,MFC 中必须显式调用消息处理器的基类实现。 我们再添加 WM_COMMAND 的处理器。假定我们窗口的菜单有一个 ID 为 IDC_ABOUT 的 About 项:class CMyWindow : public CWindowImpl { public: DECLARE_WND_CLASS(_T("My Window Class")) BEGIN_MSG_MAP(CMyWindow) MESSAGE_HANDLER(WM_CLOSE, OnClose) MESSAGE_HANDLER(WM_DESTROY, OnDestroy) COMMAND_ID_HANDLER(IDC_ABOUT, OnAbout) END_MSG_MAP() LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { DestroyWindow(); return 0; } LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { PostQuitMessage(0); return 0; } LRESULT OnAbout(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) { MessageBox ( _T("Sample ATL window"), _T("About MyWindow") ); return 0; } }; 注意,COMMAND_HANDLER 宏为你做了解析消息参数的工作。NOTIFY_HANDLER 宏类似地解析 WM_NOTIFY 的消息参数。 高级消息映射和嵌入(Mix-in)类 ATL 中一个最大的不同是任何 C++ 类都可以处理消息,不像 MFC 里消息处理的任务在 CWnd 和 CCmdTarget 间分割开来,再加上几个有 PreTranslateMessage() 方法的类。这一能力允许我们写通常被称作嵌入类的东西,以便通过向继承列表中添加类就可以向我们的窗口中增加特性。 带有消息映射的基类通常是一个以派生类名作为模板参数的模板,这样就能访问派生类中像 m_hWnd(CWindow 中的 HWND 成员)这样的成员。我们来看一下这个嵌入类,它通过处理 WM_ERASEBKGND来绘制窗口背景。template class CPaintBkgnd : public CMessageMap { public: CPaintBkgnd() { m_hbrBkgnd = CreateSolidBrush(t_crBrushColor); } ~CPaintBkgnd() { DeleteObject ( m_hbrBkgnd ); } BEGIN_MSG_MAP(CPaintBkgnd) MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd) END_MSG_MAP() LRESULT OnEraseBkgnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { T* pT = static_cast(this); HDC dc = (HDC) wParam; RECT rcClient; pT->GetClientRect ( &rcClient ); FillRect ( dc, &rcClient, m_hbrBkgnd ); return 1; // we painted the background } protected: HBRUSH m_hbrBkgnd; }; 我们来审视一下这个新类。首先,CPaintBkgnd 有两个模板参数:派生类的名字:CPaintBkgnd,和背景的颜色(t_ 前缀通常用于值类型的模板参数) 构造函数和析构函数相当简单,它们创建/销毁了一个 t_crBrushColor 颜色的画刷。接下来的消息映射处理了 WM_ERASEBKGND。最后,OnEraseBkgnd() 处理器使用构造函数中创建的画刷来填充窗口。OnEraseBkgnd() 中有两件事值得注意。首先,它使用了派生类的窗口函数(名为 GetClientRect())。我们怎么知道派生类里恰好有一个 GetClientRect() 函数?但如果没有的话,代码根本不能编译!编译器确保派生类 T 派生于 CWindow。其次,OnEraseBkgnd() 从 wParam 处得到设备上下文。 在我们的窗口中使用这一嵌入类,要做两件事。首先,我们把它加到继承列表中:class CMyWindow : public CWindowImpl, public CPaintBkgnd 然后,我们要使 CMyWindow 传递消息到 CPaintBkgnd。这叫做串联消息映射。在 CMyWindow 的消息映射里,加入 CHAIN_MSG_MAP 宏:class CMyWindow : public CWindowImpl, public CPaintBkgnd { ... typedef CPaintBkgnd CPaintBkgndBase; BEGIN_MSG_MAP(CMyWindow) MESSAGE_HANDLER(WM_CLOSE, OnClose) MESSAGE_HANDLER(WM_DESTROY, OnDestroy) COMMAND_HANDLER(IDC_ABOUT, OnAbout) CHAIN_MSG_MAP(CPaintBkgndBase) END_MSG_MAP() ... }; 任何到达 CMyWindow 映射而没有处理的消息将被传递到 CPaintBkgnd 的映射中。注意,WM_CLOSE,WM_DESTROY,和 IDC_ABOUT 不会被 串联,因为只要它们一被处理,对消息映射的搜索就会终止。typedef 是必要的,因为 CHAIN_MSG_MAP 是个接收单个参数的预处理宏;如果我们以 CPaintBkgnd 作为参数,其中的逗号会使预处理器认为我们以不止一个参数来调用该宏。 你可以在继承列表中放心的使用多个嵌入类,对每一个类使用 CHAIN_MSG_MAP 宏以使消息能够传入。这不同于 MFC,每个 CWnd 派生类只能有一个基类,而且 MFC 自动向基类传递未处理的消息。 ATL EXE 的结构 现在我们有了一个完整的(虽然不怎么有用)主窗口,我们来看看如何在程序中使用它。ATL 的可执行程序里有一个或者多个大致对应于 MFC 程序中的全局 CWinApp (通常名为 theApp)的全局变量。这一领域在 VC6 和 VC7 之间从根本上被改变了,所以我需要分别介绍这两个版本。 VC 6 的情形 ATL 的可执行程序包含一个全局的 CComModule 变量,这个变量必须命名为 _Module。我们的 stdafx.h 以此开始:// stdafx.h: #define STRICT #define VC_EXTRALEAN #include // Base ATL classes extern CComModule _Module; // Global _Module #include // ATL windowing classes atlbase.h 会包含基本的 Windows 头文件,所以不必再包含 windows.h,tchar.h 等。在 CPP 文件里,声明(译者注:应该为定义)_Module 变量:// main.cpp: CComModule _Module; CComModule 中有我们需要在 WinMain() 中调用的显式初始化/退出函数,所以我们以此为始:// main.cpp: CComModule _Module; int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, LPSTR szCmdLine, int nCmdShow) { _Module.Init(NULL, hInst); _Module.Term(); } 传给 Init() 的第一个参数仅在 COM 服务器中使用。而我们的 EXE 不是 COM 服务器,所以我们只需要传入 NULL。ATL 并不像 MFC 那样提供自己的 WinMain() 或者消息泵,所以要使我们的程序运行起来,需要创建 CMyWindow 对象并添加消息泵。// main.cpp: #include "MyWindow.h" CComModule _Module; int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, LPSTR szCmdLine, int nCmdShow) { _Module.Init(NULL, hInst); CMyWindow wndMain; MSG msg; // Create & show our main window if ( NULL == wndMain.Create ( NULL, CWindow::rcDefault, _T("My First ATL Window") )) { // Bad news, window creation failed return 1; } wndMain.ShowWindow(nCmdShow); wndMain.UpdateWindow(); // Run the message loop while ( GetMessage(&msg, NULL, 0, 0) > 0 ) { TranslateMessage(&msg); DispatchMessage(&msg); } _Module.Term(); return msg.wParam; } 以上代码中唯一与众不同的是 CWindow::rcDefault,它是 CWindow 的一个 RECT 成员。将它作为窗口的初始 RECT 就像在 CreateWindow() API 里用 CW_USEDEFAULT 表示宽和高(译者注:此处有误,CW_USEDEFAULT 是用以表示左坐标和宽的)。 ATL 在底下使用了一些汇编语言的把戏来把主窗口的句柄和与之相应的 CMyWindow 对象联系起来。在此之上就是我们可以把 CWindow 对象在线程间传来传去而不出问题,而如果在 MFC 里对 CWnd 这样干会死得很惨。 VC 7 的情形 ATL 7 把模块管理代码分散到了好几个类中。出于兼容的目的,CComModule 仍然存在,在 VC 6 里写的代码经过 VC 7 转换后并不是总能干干净净地编译过去,如果不是根本编译不过去的话。因此在这我介绍一下新的类。 在 VC 7 里,ATL 的头文件自动声明所有模块类的全局实例,而且还为你调用了 Init() 和 Term() 方法,因此这些手动步骤就不再需要了。于是我们的 stdafx.h 看起来就是这样:// stdafx.h: #define STRICT #define WIN32_LEAN_AND_MEAN #include // Base ATL classes #include // ATL windowing classes WinMain() 函数不调用任何 _Module 方法,就像这样:// main.cpp: #include "MyWindow.h" int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, LPSTR szCmdLine, int nCmdShow) { CMyWindow wndMain; MSG msg; // Create & show our main window if ( NULL == wndMain.Create ( NULL, CWindow::rcDefault, _T("My First ATL Window") )) { // Bad news, window creation failed return 1; } wndMain.ShowWindow(nCmdShow); wndMain.UpdateWindow(); // Run the message loop while ( GetMessage(&msg, NULL, 0, 0) > 0 ) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } 我们的窗口看起来像是这样: 我得承认,没什么特别激动人心的事情。为了能增添些情趣,我们会加一个能显示对话框的 About 菜单项。 ATL 中的对话框 正如已经提到的,ATL 有两个对话框类。我们将为我们的对话框使用 CDialogImpl。创建一个新的对话框类与创建一个新的框架窗口类大致相当,只不过有两处不同: 基类是 CDialogImpl 而不是 CWindowImpl 需要定义一个名为 IDD 的公用成员,其中包含有对话框的资源 ID 这是新的 About 对话框类的初始定义:class CAboutDlg : public CDialogImpl { public: enum { IDD = IDD_ABOUT }; BEGIN_MSG_MAP(CAboutDlg) END_MSG_MAP() }; ATL 没有针对 OK 和 Cancel 按钮的内建处理器,所以我们需要自己写代码,顺便还有 WM_CLOSE 的处理器,当用户点击标题条上的关闭按钮时此处理器会被调用。我们还需要处理 WM_INITDIALOG 以使对话框出现时能正确的设置键盘焦点。这是带有消息处理器的完整的类定义。class CAboutDlg : public CDialogImpl { public: enum { IDD = IDD_ABOUT }; BEGIN_MSG_MAP(CAboutDlg) MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) MESSAGE_HANDLER(WM_CLOSE, OnClose) COMMAND_ID_HANDLER(IDOK, OnOKCancel) COMMAND_ID_HANDLER(IDCANCEL, OnOKCancel) END_MSG_MAP() LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { CenterWindow(); return TRUE; // let the system set the focus } LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { EndDialog(IDCANCEL); return 0; } LRESULT OnOKCancel(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) { EndDialog(wID); return 0; } }; 我们为 OK 和 Cancel 使用同一个处理器以演示 wID 参数,该参数的值被设为 IDOK 或 IDCANCEL,这取决于哪个按钮被点击。 显示对话框和 MFC 相似,创建新类的一个对象并调用 DoModal()。回到我们的主窗口并添加一个拥有 About 菜单项的菜单,该项将显示我们的 About 对话框。我们需要添加两个消息处理器,一个是为 WM_CREATE 而另一个是为新的菜单项 IDC_ABOUT。class CMyWindow : public CWindowImpl, public CPaintBkgnd { public: BEGIN_MSG_MAP(CMyWindow) MESSAGE_HANDLER(WM_CREATE, OnCreate) COMMAND_ID_HANDLER(IDC_ABOUT, OnAbout) // ... CHAIN_MSG_MAP(CPaintBkgndBase) END_MSG_MAP() LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { HMENU hmenu = LoadMenu ( _Module.GetResourceInstance(), // _AtlBaseModule in VC7 MAKEINTRESOURCE(IDR_MENU1) ); SetMenu ( hmenu ); return 0; } LRESULT OnAbout(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) { CAboutDlg dlg; dlg.DoModal(); return 0; } // ... }; 模态对话框的一个小小的不同是在什么地方指定对话框的父窗口。MFC 里是向 CDialog 的构造函数传递父窗口,而在 ATL 中应该把父窗口作为 DoModal() 的第一个参数传递。如果像上面的代码一样不指定,ATL 使用 GetActiveWindow()(将会是我们的框架窗口 )的返回结果作为父窗口。 LoadMenu() 调用也演示了 CComModule 的一个方法:GetResourceInstance()。它返回一个含有资源的模块的 HINSTANCE,就像 AfxGetResourceHandle() 一样,默认的行为是返回 EXE 的 HINSTANCE(还有一个 CComModule::GetModuleInstance(),此函数与 AfxGetInstanceHandle()相仿 )。 注意,由于模块管理类的不同,VC6 和 7 的 OnCreate() 是不同的,GetModuleInstance() 现在是在 CAtlBaseModule 里,而我们调用的是 ATL 为我们准备好的 _AtlBaseModule 对象。 这是我们修改后的主窗口和 About 对话框: 就要到 WTL 了,我保证! 不过会是在第二部分里。因为我是在为 MFC 开发人员写这些文章,所以我认为在进入 WTL 之前,最好对 ATL 先做个介绍。如果这是你对 ATL 的第一次亲密接触,那现在也许是你自己写一些简单应用的好机会,从而可以熟悉消息映射以及嵌入类的使用。你也可以实践一下 ClassView 对 ATL 消息映射的支持,它可以为你添加消息处理器。想要在 VC 6 里开始,请右击 CMyWindow 项并选择关联菜单中的 Add Windows Message Handler。在 VC 7 里,请右击 CMyWindow 项并选择关联菜单中的 Properties。在属性窗口里,点击工具栏上的 Message 按钮可以看到一个窗口消息的列表。要为消息添加处理器,可以到消息对应的行上,点击右面的一列,使之改变为一个组合框,点击组合框的箭头,然后再点击下拉列表中的 项。 在第二部分里,我将讲解基本的 WTL 窗口类、WTL AppWizard,以及更好的消息映射宏。 修订历史 2003 年 3 月 22 日:首次发布 2005 年 12 月 15 日:更新,包括了 VC 7.1 中 ATL 的改变 ...展开收缩
(系统自动生成,下载前可以参看下载内容)

下载文件列表

相关说明

  • 本站资源为会员上传分享交流与学习,如有侵犯您的权益,请联系我们删除.
  • 本站是交换下载平台,提供交流渠道,下载内容来自于网络,除下载问题外,其它问题请自行百度
  • 本站已设置防盗链,请勿用迅雷、QQ旋风等多线程下载软件下载资源,下载后用WinRAR最新版进行解压.
  • 如果您发现内容无法下载,请稍后再次尝试;或者到消费记录里找到下载记录反馈给我们.
  • 下载后发现下载的内容跟说明不相乎,请到消费记录里找到下载记录反馈给我们,经确认后退回积分.
  • 如下载前有疑问,可以通过点击"提供者"的名字,查看对方的联系方式,联系对方咨询.
 输入关键字,在本站1000多万海量源码库中尽情搜索: