ATL&WTL第二部分 下载本文

内容发布更新时间 : 2024/12/26 18:45:50星期一 下面是文章的全部内容请认真阅读。

第二部分 - WTL 中的 GUI 基础类

内容

? ? ? ? ?

第二部分介绍 WTL 综述

开始一个 WTL EXE WTL 消息映射的增强

使用 WTL AppWizard 可以得到什么

? ? ?

通历向导(VC 6) 通历向导(VC 7) 检查生成的代码

? ? ? ?

CMessageLoop 内幕 CFrameWindowImpl 内幕 回到时钟程序 UI 更新

? ?

控制时钟的新菜单项 调用 UIEnable() ? ? ?

关于消息映射的最后注意事项 下一站,1995 修订历史

第二部分介绍

好,是实实在在地讲述 WTL 的时候了!在这部分里,我会介绍写一个主框架窗口的基础知识,以及 WTL 引入的比较受欢迎的改进,比如 UI 更新和更好的消息影射。为了最大程度地掌握本部分的内容,你应该安装 WTL 以使其头文件处于 VC 的搜索路径中,而且 AppWizard 也在适当的目录下。WTL 的分发包中附有如何安装 AppWizard 的说明,请参考该文档。

记住,如果你安装 WTL 或者编译示例代码时遇到了任何问题,请在张贴你的问题之前阅读第一部分的 ReadMe 一节。

WTL 综述

WTL 的类可以分为几个主要的类别:

1. 框架窗口的实现 - CFrameWindowImpl, CMDIFrameWindowImpl 2. 控件封装 - CButton, CListViewCtrl 3. GDI 封装 - CDC, CMenu 4. 特殊的 UI 特性

- CSplitterWindow, CUpdateUI, CDialogResize, CCustomDraw 5. 工具类以及宏 - CString, CRect, BEGIN_MSG_MAP_EX

本文将深入到框架窗口中去,顺便提及一些 UI 特性和工具类。大多数的类都是独立的,不过也有一些像 CDialogResize 这样的嵌入类(mix-in)。

开始一个 WTL EXE

如果你不使用 WTL AppWizard (稍后我们就会提到它),那么一个 WTL EXE 一开始会很像一个 ATL EXE。如同第一部分中的那样,本文中的示例代码是另一个框架窗口,不过为了展示一些 WTL 的特性,较之前者不再那么微不足道。 在本节里,我们会从头开始一个新的 EXE。主窗口会在其客户区显示当前的时间。下面是一个基本的 stdafx.h:

#define STRICT

#define WIN32_LEAN_AND_MEAN #define _WTL_USE_CSTRING

#include // base ATL classes #include // base WTL classes

extern CAppModule _Module; // WTL version of CComModule #include // ATL GUI classes

#include // WTL frame window classes #include // WTL utility classes like CString #include // WTL enhanced msg map macros atlapp.h 是要包含的第一个 WTL 头文件。它包含了用于消息处理的类和一个继承自 CComModule 的类 CAppModule。如果你计划使用 CString那就还应该定义 _WTL_USE_CSTRING,因为 CString 定义在 atlmisc.h 里,而在 atlmisc.h 包含的其他头文件里有的特性会使用到 CString。定义 _WTL_USE_CSTRING 使得 atlapp.h 会前向声明 CString 类,从而使其他的这些头文件知道一个 CString 究竟是什么。

(注意,我们需要一个全局的 CAppModule 变量尽管在第一部分里这不是必需的。CAppModule 的一些特性与我们所需的空闲处理以及 UI 更新相关,所以我们需要 CAppModule 的存在)

接下来我们来定义我们的框架窗口。像我们这样的 SDI 窗口继承

自 CFrameWindowImpl。窗口类名是使用 DECLARE_FRAME_WND_CLASS 而不是DECLARE_WND_CLASS 来定义。这儿是 MyWindow.h 里我们窗口定义的开头: // MyWindow.h:

class CMyWindow : public CFrameWindowImpl {

public:

DECLARE_FRAME_WND_CLASS(_T(\),

IDR_MAINFRAME);

BEGIN_MSG_MAP(CMyWindow)

CHAIN_MSG_MAP(CFrameWindowImpl) END_MSG_MAP() };

DECLARE_FRAME_WND_CLASS 有两个参数,窗口类名(可以为 NULL,ATL 会替你生成一个类名),和一个资源 ID。WTL 会根据此 ID 去寻找图标、菜单以及加速键表,并在窗口创建时加载它们。还会根据此 ID 寻找一个字符串,然后使用该串作为窗口的标题。我们还把消息串联到CFrameWindowImpl,因为它有自己的一些消息处理器,尤其是 WM_SIZE 和 WM_DESTROY。

现在我们来看 WinMain()。它和第一部分中的 WinMain() 极其类似,只是创建主窗口的调用存在差异。

// main.cpp:

#include \#include \

CAppModule _Module;

int APIENTRY WinMain ( HINSTANCE hInstance, HINSTANCE hPrevInstance,

LPSTR lpCmdLine, int nCmdShow ) {

_Module.Init ( NULL, hInstance );

CMyWindow wndMain; MSG msg;

// Create the main window

if ( NULL == wndMain.CreateEx() )

return 1; // Window creation failed

// Show the window

wndMain.ShowWindow ( nCmdShow ); wndMain.UpdateWindow();

// Standard Win32 message loop

while ( GetMessage ( &msg, NULL, 0, 0 ) > 0 ) {

TranslateMessage ( &msg ); DispatchMessage ( &msg ); }

_Module.Term(); return msg.wParam;

}

CFrameWindowImpl 的 CreateEx() 方法采用了最常用的缺省值,因而我们不需要指定任何参数。CFrameWindowImpl 还会处理前文提到的资源加载事宜,所以现在你应该使用 IDR_MAINFRAME 这一 ID 生成一些伪资源,或者使用随本文附带的示例代码。

如果你马上运行,就可以看到主框架窗口了,当然,它实际上还没有做任何事情。我们需要加入一些消息处理器来干活儿,所以现在是研究 WTL 消息映射宏的好时机。

WTL 消息映射的增强

在使用 Win32 API 时,既令人讨厌又易于出错的事情之一就是从随消息一起发送过来的 WPARAM 和 LPARAM 数据中拆封参数。不幸的是,ATL 并未提供更多的帮助,除去 WM_COMMAND 和 WM_NOTIFY 之外,我们仍然需要从其他所有的消息中拆封数据。不过,WTL 正好在这儿对我们施以援手!

WTL 的增强消息映射宏在 atlcrack.h 文件中(此名字来源于 “message cracker”,是一个应用于 windowsx.h 中类似的宏的术语)。要使用这些宏的第一个步骤在 VC 6 和 VC 7 里是不一样的,在 atlcrack.h 中的以下提示解释了这一不同:

对于 ATL 3.0,使用了解拆处理器的消息映射必须使用 BEGIN_MSG_MAP_EX。 对于 ATL 7.0/7.1,你可以为 CWindowImpl/CDialogImpl 的派生类使

用 BEGIN_MSG_MAP,但是对于不是派生于CWindowImpl/CDialogImplbut 的类则必须使用 BEGIN_MSG_MAP_EX。

所以,如果你在使用 VC 6,你需要这样改动你的 MyWindow.h: // MyWindow.h, VC6 only:

class CMyWindow : public CFrameWindowImpl {

public:

BEGIN_MSG_MAP_EX(CMyWindow)

CHAIN_MSG_MAP(CFrameWindowImpl) END_MSG_MAP()

};

(_EX 宏对于 VC 6 来讲是必需的,因为包含于其中的某些代码是消息处理器宏需要使用的。出于可读性的原因,这里就不列出 VC 6 和 VC 7 版本的头文件了,因为它们仅仅是一个宏上面的不同。只需记住_EX 宏在 VC 7 里是不需要的即可。)

对我们的时钟程序来说,我们需要处理 WM_CREATE 并设置一个定时器。WTL 把针对一个消息的消息处理器命名为 MSG_ 后随消息名,比如MSG_WM_CREATE。这些宏仅接受处理器的名字。我们来为 WM_CREATE 添加一个处理器:

class CMyWindow : public CFrameWindowImpl {

public:

BEGIN_MSG_MAP_EX(CMyWindow) MSG_WM_CREATE(OnCreate)

CHAIN_MSG_MAP(CFrameWindowImpl) END_MSG_MAP()

// OnCreate(...) ? };

WTL 的消息处理器看起来很像 MFC,每个处理器都根据随消息传入的参数有一个不同的原型。不过,由于没有向导来写处理器,我们不得不自己来查找原型。幸运的是 VC 可以帮上忙。将光标(注:此处原文错误,不应该是光标[cursor],而应该是插入符[caret])放在 “MSG_WM_CREATE” 文本上再按 F12 会转到宏的定义处。在 VC 6 里,VC 会先重新编译工程以构建浏览信息数据库。这一工作一旦完成,VC 就会在 MSG_WM_CREATE 的定义处打开 atlcrack.h: #define MSG_WM_CREATE(func) \\ if (uMsg == WM_CREATE) \\ { \\

SetMsgHandled(TRUE); \\

lResult = (LRESULT)func((LPCREATESTRUCT)lParam); \\ if(IsMsgHandled()) \\ return TRUE; \\

}

带下划线的是最重要的一行,那是对处理器的实际调用,它告诉我们处理器会返回一个 LRESULT 并接受一个 LPCREATESTRUCT 类型的参数。注意,没有像 ATL 的宏所使用的 bHandled 参数。SetMsgHandled() 函数替代了该参数,很快我们就要解释这件事情。

现在我们可以为窗口类添加一个 OnCreate() 处理器: