为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

WTL开发者指南

2012-04-09 34页 doc 279KB 37阅读

用户头像

is_431437

暂无简介

举报
WTL开发者指南WTL开发者指南 注:本文是袁晓辉根据Eamon O’ Tuathail的WTL Developer’s Guide(www.clipcode.biz ) 翻译而来,发布在www.farproc.com。作者翻译本文仅仅是为自己和其他WTL爱好者学习之用,请勿用于商业用途。你可以转载本文,但必须保证本文的完整性,并保留该声明!   第1章 WTL概述 目标 该章的目标为: Ÿ       WTL在整个VC++开发中所处的位置 Ÿ       WTL的目标 Ÿ       WTL和其他用户界面(UI)开发技术的对比 Ÿ     ...
WTL开发者指南
WTL开发者指南 注:本文是袁晓辉根据Eamon O’ Tuathail的WTL Developer’s Guide(www.clipcode.biz ) 翻译而来,发布在www.farproc.com。作者翻译本文仅仅是为自己和其他WTL爱好者学习之用,请勿用于商业用途。你可以转载本文,但必须保证本文的完整性,并保留该声明!   第1章 WTL概述 目标 该章的目标为: Ÿ       WTL在整个VC++开发中所处的位置 Ÿ       WTL的目标 Ÿ       WTL和其他用户界面(UI)开发技术的对比 Ÿ       介绍WTL的安装 Ÿ       介绍WTL安装了什么 Ÿ       列举可用的开发资源 Ÿ       对后面各章的介绍   概述 以前Visual C++开发者往往选择MFC,因为它提供的功能涵盖了非常广的编程技术领域而模板库所涉及的领域相对狭窄。 最近模板库涉足的领域和质量都有很大发展,因而越来越多的人选择了模板库。 ATL的目标是提供快速紧凑的COM组件,ISO C++的STL提供集合(collections),VC++ OLE DB Data Consumer and Provider模板提供数据库支持。与之相对应,MFC提供了一个C++类库,这个类库包含对COM创建、集合类(collection class)、数据库和用户界面的普遍支持。 大部分的高级开发人员更喜欢较新的模板开发方式。MFC很庞大、臃肿、对线程不友好,并且,基本上已经过时了!模板的方法既快速(当正确设计时)、有弹性而且涵盖了所有最新的技术,拿来应对新的开发需要是再好不过的了。 到目前为止,让模板狂热分子头疼的主要问题在于如何创建图形用户界面(GUI)。ATL确实提供了一个轻量级的Win32/64 windowing 包装类,但是它并没有涵盖所有的UI需求。ATL开发者为了琐碎的用户界面处理不得不重操MFC UI编程或者用VB给自己的ATL COM做前端,这两种方法都不能完全令人满意。 进入正题,开始说窗口模板库(Windows Template Library)。WTL是一个居于Win32/64 API之上的一个包装类和方便易用的增强模板的高级集合,它对图形用户界面提供了广泛的支持。WTL不但保持着模板库的真正血统,小巧,快速,不具侵入性而且包括了最新的UI理念,非常好地支持多线程。你可以单独使用WTL或和ATL、STL、VC++ Data Templates、第三方类库,以及你自己的类库一起使用以便利用他们各自的优点。WTL通常不依赖外部的DLLs,如果你用WTL开发程序的话,你提供给最终用户的可以只有一个EXE文件。   WTL的目标 WTL之于用户界面就像ATL之于COM、STL之于collection。和它的堂兄一样,WTL需要一段时间来学习,但是一旦掌握了,就没有比它更好的开发高级程序的途径了。从本质上说,WTL完成了三个重大任务:     提供了一个应用程序框架(framwork)     集合了UI功能     包装了windows控件、系统对话框和GDI对象 让我们逐个来看。   提供了一个应用程序框架   WTL提供了一个轻量级的广泛的应用程序框架,这个框架为基于它的程序提供了非常有价值的工具。它的目标既不是像MFC框架那样难以理解也不像手工写WinMain那样原始。 WTL包含了一系列的C++模板和一个WTL AppWizard,AppWizard向程序开发者提出几个问题,然后据此生产VC++工程和程序源代码,源代码里多数是WTL模板类的实例或继承类。一般来说,开发者仅仅需要改变AppWizard生产的代码,而那些很少需要改动的“样板”式的代码都被WTL模板封装了。 WTL中有一个管理整个模块(一个DLL或EXE)的类,这个类在AppWizard生成的代码中实例化,在自动生成的WinMain中被初始化和终结。一个WTL程序也可以作为COM server,支持可编程的应用程序。WTL提供了消息处理机制,支持消息过滤和空闲(on-idle)功能,WM_SETTINGCHANGE引发的改变通知也可以被截获。 MFC提供了一个“文档-框架-视图”结构,WTL也提供框架(比如一个包括菜单条、工具条和状态条的顶层窗口)和视图,但是根本不支持文档。MFC的那套方法往往妨碍高级的开发者,它的文档是基于二进制数据的序列化(serialize),在当今互联网的世界里,基于XML/XSL的文档格式越来越流行,对于弹性化存储机制的需求也越来越多(可能根本就不是存储在本地磁盘上的),文档可能需要运用大量的远程机制,比如WebDAV,FTP等,进行存储。WTL没有提供任何关于数据或存储格式的功能和机制,程序开发者可以自己编写代码,比如利用高效的Win32 API WriteFileGather/ReadFileScatter或XML解析器来根据需要做这些工作。 WTL支持基于对话框的程序,单文档界面(SDI)和多文档界面(MDI)。对于SDI它同样支持多线程SDI,每个SDI窗口一个线程,多数高级的应用程序很有可能用到这种构架(比如,模仿Word2000的方法)。WTL不支持多线程MDI构架,即不支持一个MID子窗口一个线程,但这是可以理解的,比如这样可以避免线程和MDI父子窗口之间交互的许多问题。   集合了UI功能 WTL提供了一组实现现代的用户界面所必不可少的特性。 WTL提供了一个框架窗口用于管理一个包含了命令条(增强的菜单条)、工具条的rebar,一个状态条,一个或多个视图(views)。视图可以基于一个普通的空窗口RechEidt,ListView,TreeView之类的控件。如果使用了多个视图,WTL的分割功能就派上用场了,视图也支持滚动,状态条可以是一个pane或多个panes。 WTL也支持传统的菜单条和工具条,但是它的主菜单表现概念是基于命令条的。一个命令条可以容纳菜单和工具条图标,可以放在一个rebar里显示并四处移动。如果一个菜单项目和一个工具条按钮拥有同样的命令ID的话,菜单项目旁边会自动显示对应的图标。WTL支持可停泊或浮动的命令条,WTL的命令条和Internet Explorer中的很像但和Word2000或Visual Studio的(更令人向往)不一样,WTL不支持可以隐藏不常用的项目的菜单(比如Word2000的)。动态数据交换的概念允许数据在可见的UI控件和C++数据成员之间双向传递,和MFC的DDX很相近。可以用ATL生成对话框,包括那些支持ActiveX控件和窗口容器,利用Visual C++的Resource View可以用来在对话框资源上布局控件、通用控件和ActiveX控件,利用ATL Window Message Handler Wizard来映射这些控件的消息。 WTL提供了用来管理属性薄、属性页创建和向导创建的模板创建。 打印机设备场景、打印预览、devmode处理和打印作业管理功能可以用来实现文档打印。 使用特殊的设备场景、文件管理器和增强metafile信息类可以支持增强的metafile。   包装了windows控件、系统对话框和GDI对象 ATL提供了对普通窗口的控制但是没有提供对基于不同窗口类的窗口的特殊支持,开发者不得不根据需要手工编写SnedMessage调用。对于和Edit或TreeView的通讯他们不得不使用ATL的CWindow并且调用Win32的SendMessage以EM_LIMITTEXT或VM_SETITEM还得担心消息的参数是否正确(SendMessage没有类型检查,全部为WPARAM,LPARAM)。当程序接收到它们时也是“裸数据”- wParam和lParam被送到了消息队列,必须通过代码来转换数据为合适的类型,这时还得保证转换对于每个消息是正确的 一些ATL开发者都使用从ATLCON例程中提取出的代码,这些代码提供了对Windows UI 元素的一些封装封装。WTL现在以及进化出了一整套的广泛的封装类,这些类实现了对所有标准和通用窗口控件、系统对话框和所有GDI对象以及其他的封装。WTL中无论是对edit,button,listbox,treeview,listview等还是对通用文件对话框、颜色对话框、字体对话框等还是对DC,pen,brush,region,font等都有对应的包装类。 另外,WTL还提供了一整套的message crackers,进入窗口的消息被映射到指定的处理函数。使用ATL时,消息处理函数收到的是lParam和wParam,而使用WTL的message cracker,消息处理函数的参数都是具体消息的具体类型。比如:WM_LBUTTONDOWN消息有一个CPoint类型的参数(CPoint是WTL对Win32 POINT结构的包装-WTL包装了每一个通用的结构)。注意,ATL Windows Message Handler Wizard 使用ATL的原始消息映射,非WTL的message crackers,VC++下一版可能对向导支持会好一些。 许多的开发者选择MFC仅仅是为了使用CString,WTL自己也提供了对CString的实现,并且是几乎和MFC一样的实现。WTL的CString支持ASCII,MBCS,UNICODE和自动化的BSTR,支持这些类型间的相互转换,支持字符串连接、截断、和比较,支持printf一样的格式化处理,支持弹性的内存管理。   WTL,ATL和Win32/64 API之间的关系 WTL是基于ATL和Win32、64API的,ATL是基于Win32/64 API的,当谈到它们之间的关系时,我们应该从源代码和二进制两个视角来讨论。 源代码 Win32/64 API是成千上百的C函数的集合,涵盖了相当大的范围,包括我们感兴趣的COM和windowing。 ATL是C++模板的集合,几乎就是为COM编程量身定做的,但是也包括广泛的底层windowing功能的支持。 WTL是C++模板的集合,它的关注点在高层的windowing功能,WTL不依赖于COM,但是可以和COM一起使用。只要你不在WTL中选择支持“Com Server”,CoInitialize就不会被调用。 WTL使用ATL的windowing服务,所以要使用WTL,你必须访问ATL模板。 ATL调用Win32/64 API,WTL调用ATL模板和Win32/64 API,你的程序代码调用WTL,ATL和Win32/64 API。 当你运行了WTL AppWizard,他给你生成一些源文件,这些源文件将成为你程序代码的一部分,这些生成的代码调用WTL,构建程序框架。 WTL支持Windows 2000,Windows Me,Windows NT4,Windows 98,Windows95, OSR 2和“经典”windows 95,WTL并不是只支持它们 “最小公分母”,而是提供#define(比如 _RICHEDIT_VER, _WIN32_IE, _WIN32_WINNT and WINVER)来决定使用那些Win32/64的特性。WTL不会自动判别被安装到的操作系统的版本或IE的版本(比如,它不使用LoadLibrary /GetProceAddress 或GetVersionEx APIs来判别)。程序开发者必须指定#define宏,而且在编译期间已经假定程序在正确版本的客户机上可以正常运行,否则不能。   举个例子,菜单中的“>>”符号(v型符号),当一个命令条因为窗口大小改变而被截短时如果不使用v型符号,超出窗口边界的那部分命令条就别直接“剪掉”了。 V型符号是一个包含两个大于号的标志,当被按下时,就弹出一个菜单菜单上包含由于窗口大小改变而被从命令条尾部截去的那些项目。如果包含了IE5,v型符号就被支持,反之不支持。 在WTL程序中,如果_WIN32_IE 别定义为0x0500或更高(比如,在stdafxh里)那么v型符号在必要时就会显示。   二进制 ATL和WTL是作为一组包含C++模板的头文件的形式发布的。ATL中有少数的类可以作为一个独立的DLL来使用,但是多数的程序员也把它们的源代码包含到自己的工程里面去,对于WTL程序来说,这也是被推荐的。WTL没有可以作为独立的DLL的东西,所以WTL的C++类(并非全部)将被包含进每个程序。当你编译WTL程序,你最终会得到一个EXE或DLL,这就是你唯一需要交付最终用户的程序,不依赖外部库,当然除去操作系统自带的DLL,比如kernel32.dll,但是这些库基本上都存在(如果不存在,windows本身也许就无法正常工作了)。一个例外:如果你需要使用浮点数,你不得不使用C语言运行库(CRT)。   WTL-ATL,MFC,VB,Java,DHTML等的抉择 ……(省略废话数百字)   为什么选择WTL? WTL不像MFC那样笨重,最终的应用程序也比较小巧。对于高级程序,WTL给你使用API的机会,但是比纯粹的API编程节省时间。 Win32 API可以直接使用,但是有时候它们看起来相当独立,很难决定如何将它们结合起来使用。 WTL提供了更多的可以完成同样操作的功能。 由于被调用的方法是在编译期而不是运行期决定的,这就是它在效率和代码规模方面占有优势,模板库正在变得越来越流行。   安装WTL WTL的发展很快,它的发行机制也在时常变化。 未来的发行模式 很可能下一版VC++发布时将会整合进WTL,因此,它可能会和ATL,STL,Data Templates一起自动安装。 现在的安装机制 当前,WTL是作为Microsoft Platform SDK (January 2000 或更高)的一部分发布的,需要手动分三步安装,未来可能会自动完成。 第一步-取得文件 可以从MSDN光盘的第三张或http://msdn.microsoft.com得到安装文件。你可以选择全部安装这个SDK或仅仅安装包含Windows Template Library的部分。(译者注:你也以在www.farproc.com下载WTL7.1安装包)   注意:WTL7.1安装的后面步骤与作者原文有出入,所以下面关于安装的就没有安装原文翻译   第二步-WTL Appwizard 安装后打开WTL的安装(比如c:\Program Files\Microsoft Platform SDK)进入对应的AppWizXX目录,其中XX为你的VC的版本号,比如你用的是VC6.0的话就进入AppWiz60,你会看到一个setup.js文件,点击运行它就可以为你自动安装WTL的AppWizard了。如果你无法运行这个文件或想手工安装,就复制这个目录下的AtlApp60.awx到“……\Microsoft Visual Studio\Common\MSDev98\Template”目录下也可以的。 然后打开vc,在new对话框中就可以看到“ATL/WTL AppWizard”选项了。     第三步-更新路径 第三步是设置路径以便让编译器找到WTL头文件。WTL是一个包含C++头文件(没有.cpp文件)的模板库,当编译时,编译器需要找到这些文件,这些文件在WTL安装目录\Src\WTL\include下,你可以在你的源文件里包含这个全路径(不推荐,但也可以),或把这个路径添加到VC的“Include Files”里,如下图   WTL安装了什么 WTL安装后,会有一个或若干个AppWizXX目录,这是WTL AppWizard文件的所在地;一个Include目录为WTL的头文件(也就是源文件)所在;还有一个samples,一些WTL程序的例子。   资源 目前WTL的可用资源比较少,不过这种情况很可能在短期内改变。 开发工具 微软的Platform SDK包含了WTL本身,除此之外,没有其他的可用开发工具了。相信在不远的将来,许多基于MFC的开发工具会转向WTL的。 有趣的网址 一些Internet网址包含了一些有趣的WTL资料: http://www.develop.com/dm/dev_resources.asp(非常全面) http://www.idevresource.com/wtl http://www.codeproject.com/wtl http://www.argsoft.com/Wtl/DocView.html http://www.sellsbrothers.com/tools/index.htm#wtl   新闻组和讨论列表 没有专门针对的WTL编程新闻组和讨论列表,但是那些以ATL为内容的也通常会涉及WTL相关的内容。 最好的ATL讨论列表为: http://discuss.microsoft.com/archives/atl.html 最好的ATL新闻租: news://msnews.microsoft.com/microsoft.public.vc.atl   书籍 除了你正在阅读的《WTL开发者指南》目前没有其他的WTL书籍了。希望随着WTL的逐步普及,更多的书商会提供WTL的书籍。 许多的ATL书籍所介绍的ATL windowing是WTL的重要基础。最好的有: ¨          “Professional ATL COM Programming”, Dr. Richard Grimes, Wrox Press, ¨          1998, ISBN: 1-861001-40-1 (he also has a beginner’s guide and a reference ¨          manual published with Wrox) ¨          “ATL Internals”, Brent Rector and Chris Sells, Addison-Wesley, 1999, ISBN: ¨          0-201-69589-8 ¨          “Creating Lightweight Components with ATL”, Jonathan Bates, SAMS, 1999, ¨          ISBN: 0-672-31535-1 [interesting discussion of adding support for Active ¨          Documents to ATL applications] ¨          “Inside ATL”, George Shepherd and Brad King, Microsoft Press, 1999, 1- ¨          57231-858-9   期刊 Visual C++开发者期刊(http://www.vcdj.com)在2000年4月发表过一篇WTL的文章。 所面向的读者 本指南面向有经验的,熟悉C++及C++模板、ATL、Win32 windowing 和MFC UI编程的软件开发人员。   章节内容 第2章-Win32 SDK windowing 回顾Win32 windowing的核心概念,在进入ATL和WTL编程之前必须对此有清晰的理解。 第3章-ATL windowing 介绍ATL在windowing方面的概念。包括创建窗口和对话框、处理消息、消息映射的处理及超类/子类。WTL中并没有重写这些概念,只是把ATL中windowing方面的已有功能进行了扩展和扩充。 第4章-WTL快速之旅 对WTL编程进行了概述,引入了WTL构建过程(build process)、应用程序体系结构,还有一些其他东西,比如CRT的使用、WTL模板和类、WTL的CString、宏的使用。 第5章-WTL Appwizard 不同的AppWizard选项生成的代码。该章分析每个AppWizard选项以及不同的选项对生成的代码的不同影响。 第6章-对话框和控件 看看在WTL中对话框和控件是如何工作的。讲解标准、通用、ActiveX控件、通用对话框、属性页等的使用方法。该章还包括了message crackers的应用、DDX、数据验证以及如何扩展它们。 第7章-图形初步 如何输出图形。该章介绍了WTL中GDI对象的模板类,比如CDC,CPen,CBrush,CFont,CBitmap,CPalette和CRgn。介绍在客户区绘画、绘画、完全的窗口绘画以及处理增强metafiles时所用到的CDC的派生类。 第8章-深入WTL 分析WTL的头文件,以这些模板的设计方法。 第9章-臭虫和建议 列举WTL当前版本中的已知bug,提出一些扩展WTL功能的或大或小的建议。 第2章 Win32 SDK windowing 目标 Ÿ            本章的目标为: Ÿ            回顾windowing的基本概念 Ÿ            引入windowing术语 Ÿ            解释在Windows下用户界面是如何构建的 Ÿ            描述Windows操作系统如何处理消息队列、子类、超类、消息循环和如何进行窗口管理等 Ÿ            分析线程和窗口如何交互 Ÿ            讲解简原始绘画是如何工作的   Windowing基本概念 WTL是建立在ATL windowing的基础之上的,而ATL又是建立在Win32 SDK基础之上的。在探索WTL之前,回顾在Windows操作系统家族里windowing是如何工作的是很重要的,这就是我们章要探讨的问题。如果能清晰地理解一些重要的Win32概念的话,我们在来看ATL和WTL的结构就会轻松得多。   窗口的类型 从最基本的说起,一台运行着Windows操作系统的PC的屏幕上的任何东西要么是一个窗口,要么是一个窗口上的原始绘画。Win32 API提供了创建、管理和销毁窗口以及表现窗口内的图形的概念。每一个用户界面元素(控件)都存在与一个独立的窗口中,最终用户可能认为存在许多类型的窗口-图形输出窗口、用户界面标准控件、(按钮,单选框等)、通用控件(ListView,TreeView)和ActiveX控件(ActiveX的一个特例是“无窗口”,可以没有自己的窗口,而是存在于他的容器窗口中),其实它们都是遵循同一套规则的窗口的实例而已。   窗口的层次 窗口在屏幕上是按层次排放的。最顶层的是桌面窗口,一个大图标模式的listview控件。应用程序的顶级窗口和它们的子窗口组成了这个有OS维护的窗口层次树的节点。 当你创建窗口时,你必须指定它的父窗口,新的窗口将位于它的父窗口所在的节点之下,当一个窗口被销毁时它的子窗口自动被销毁。   窗口类 一个窗口类是对一个窗口如何工作的重要方面的描述。你创建的每一个窗口必须有一个窗口类。操作系统为标准控件提供了窗口类,比如Button,Edit和ListBox以及通用控件ListView,TreeView,CalenderPicker 窗口类是用RegisterClassEx函数注册,用UnregisterClass取消的。 ATOM RegisterClass( //返回一个标识该窗口类的ATOM CONST WNDCLASS *lpWndClass // IN:窗口类结构指针 ); BOOL UnregisterClass( / LPCTSTR lpClassName, // WNDCLASS中的lpszClassName HINSTANCE hInstance // 窗口基本 ); 有时一个窗口类也被叫做窗口“模板”,用编程的术语来说,一个窗口类是一个Win32结构(并非一个C++结构)。窗口类中的最重要部分是窗口过程(window procedure),还包含一个用于程序最小化时显示的图标、背景刷子、默认鼠标指针和拥有者的hInstance(包含WndProc的Exe或Dll)。一个进程可以创建一个窗口类,然后基于它实例化窗口。   typedef struct _WNDCLASS { UINT style; // 包含更多设置的掩码 // (比如 CS_NOCLOSE – 不在控制菜单中显示关闭 WNDPROC lpfnWndProc;//窗口过程 int cbClsExtra; //窗口类的附加字节 int cbWndExtra; //每个窗口的附加字节 HINSTANCE hInstance;// 包含窗口过程的EXE/DLL 的实例句柄 HICON hIcon; // 窗口类的图标 HCURSOR hCursor; // 窗口类的鼠标指针 HBRUSH hbrBackground; // 背景如何画 LPCTSTR lpszMenuName; //菜单资源名称 LPCTSTR lpszClassName; // 类名 } WNDCLASS, *PWNDCLASS; 重要的Style包括 CS_SAVEBITS,CS_NOCLOSE,CS_CLASSDC,CS_OWNDC,CS_PARENTDC(译者注:具体解释省略,参看MSDN) 附加的字节可以存储在窗口类中和该类的每个窗口中。类名在以后窗口窗口时有用-作为CreateWindowx[Ex]的一个参数   窗口过程 窗口消息不断地发送到窗口上,窗口通过窗口过程来处理它们。窗口过程是一个程序和一个窗口类的特定函数,这个函数到一个特定的消息就进行相应的处理(通常要调用另外的函数来处理),对不感兴趣的消息直接转交默认的处理函数(DefWindowProc)。很正常,多个窗可能是基于同一个窗口类的,也就是说多个窗口使用一个窗口过程来处理消息(通过一个HWND类型的参数区分是属于哪个窗口的消息)。   窗口 窗口是通过调用 CreateWindowEx 创建出来的,创建时必须指定一个已经注册过的窗口类名(这个参数不能为NULL) HWND CreateWindow( LPCTSTR lpClassName, // 窗口类名 LPCTSTR lpWindowName, // 窗口名称,显示在标题栏 DWORD dwStyle, // 窗口的风格 int x, // 窗口相对于父窗口的位置(xy坐标) int y, int nWidth, nHeight, // 窗口大小 HWND hWndParent, // 父窗口 HMENU hMenu, // 菜单句柄 HINSTANCE hInstance, // 和该窗口相关联的模块(EXE/DLL)句柄 LPVOID lpParam // 自定义数据,将作为WM_CREATE的参数 );   子类 当基于一个窗口类创建了一个窗口后,这个窗口就记录了一个窗口类中的窗口过程的指针。可以用子类(subclass)在不重新编写窗口类的前提下修改窗口的行为。子类通过调用一个函数用新的窗口过程替换原来的,这样就可以在新的窗口过程里收到消息,进行处理然后再传递给原来的窗口过程。这可以用在你自己写的窗口类中或那些系统提供的预定义的控件上。   超类 超类可以用来扩充基窗口类的功能。超类自己的窗口过程收到一个消息后可以直接处理或调用基类的窗口过程。 我们通常使用子类和超类来修改一些预定义的窗口控件的行为,比如editbox和lsitbox。子类和超类只能在同一个进程中使用。   消息队列 每个调用了Win32窗口函数的线程都拥有自己的消息队列。一个创建了窗口的线程的消息队列被用来输送这个窗口的消息。   消息循环 每个拥有消息队列的线程通过消息循环来处理传递过来的消息,这个需要调用GetMessage函数,这个函数是阻塞的,直到有消息进来为止,然后调用DispatchMessage,结果是适当的窗口过程被调用。 MSG msg; while (GetMessage(&msg, 0, 0, 0)) DispatchMessage(&msg); 这个消息循环同样是COM中单线程单位(single-threaded apartments)的基础。如果一个组件被设计为被单线程调用,然而程序内外的多个线程想调用它时,一个隐形的窗口就被创建出来了,所有对指定COM的调用都被作为消息POST到这个窗口,那个对象可以一个一个地提取并处理它们。   捕获和焦点 一般来说,消息被发送到它们所发生的窗口,对于用户输入来说,就是焦点窗口。对鼠标消息来说,如果每个程序都想在收到鼠标按下消息后检测到对应的鼠标抬起消息,可以调用SetCapture来实现。   窗口风格 程序开发者在创建窗口是指定的形形色色的窗口风格会影响窗口的行为和外观。总共有两类窗口风格,标准风格(比如WS_CHILD)和扩展风格(比如WS_EX_PALETEWINDOW)。在WTL/ATL中“窗口风格”有另外一个术语,叫“特征(traits)”。   窗口属性 操作系统为一个窗口维护多组命名的属性。属性的名称是程序定义的,属性的值是个HANDLE,通常是指向程序分配并填充的一块内存的指针。   例子-用SDK写窗口 这例子演示了使用纯Win32 API编写窗口的概念。以后当我们使用ATL和WTL时再回头看看当初是如何用SDK调用手工实现的会很有帮助。 WindowsWithSDK工程是一个自动生成的简单的“Hello world”工程。在Visual C++中用 New Workspace命令,选择Win32 Application,然后选择“A typical ‘Hello World!’”,然后自动生成代码。它只是简单地初试化了一个WNDCLASSEX结构,然后调用RegisterClassEx注册了这个窗口类。然后调用CreateWindow来窗口窗口,它有一个消息循环来处理消息,并发送到窗口过程。 InstanceSubclassing工程也是同样的方法生成后,做了些更改来演示实例子类。这个工程有多写了一个窗口过程MySubclassWndProc,它处理WM_LBUTTONDOWN消息并发送所有其他的消息给原来的窗口过程(CallWndProc) LRESULT CALLBACK MySubclassedWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){ switch (message) { case WM_LBUTTONDOWN: OutputDebugString( TEXT("Mouse Down detected in subclassed WndProc\n")); break; default: return CallWindowProc(MyWndProc, hWnd, message, wParam, lParam); } return 0; } 子类是通过在窗口创建后调用SetWindowLongPtr实现的。 hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); SetWindowLongPtr(hWnd, GWL_WNDPROC, (LONG_PTR)MySubclassedWndProc);   GlobalSubclassing工程演示了改变一个注册后的窗口类的窗口过程。 首先像以前一样注册窗口类,用SetClassLongPtr来改变窗口类设置。第一个窗口创建了,然后调用SetClassLongPtr来改变所有基于该类的新窗口的窗口过程(注意,第一个窗口仍然会使用原来的窗口过程,并没有受到SetClassLongPtr的影响)。 hWnd1 = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);   SetClassLongPtr(hWnd1, GCLP_WNDPROC, (LONG_PTR)MySubclassedWndProc);   hWnd2 = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);   SuperClassing工程演示了超类化已经存在的窗口类。首先WNDCLASSEX结构的大小域被正确地初始化,然后用GetClassInfoEx提取出了一个已经存在的窗口类的信息,复制一些域到新的WNDCLASSEX结构并设置窗口过程指向一个新的函数,并调用RegisterClassEx注册了这个新窗口类。在新的窗口过程里,在调用原来的窗口过程之前加了一些代码。 wcex_base.cbSize = sizeof(WNDCLASSEX); GetClassInfoEx(hInstance, szWindowClass, &wcex_base);   CopyMemory(&wcex_superclass, &wcex_base, sizeof(WNDCLASSEX)); wcex_superclass.lpfnWndProc = (WNDPROC)MySuperclassWndProc; wcex_superclass.lpszClassName = TEXT("SuperClassName"); RegisterClassEx(&wcex_superclass);   hWnd = CreateWindow(TEXT("SuperClassName"), szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0,   CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);   MessagesAndThreads工程在原来的线程里创建了一个窗口然后有开了一个线程。 hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); HANDLE hMyThread; DWORD MyThreadID; hMyThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) MyThreadStartProc, hInstance, 0, &MyThreadID); 在新的线程里创建了一个新的窗口,两个线程都拥有自己的消息循环。规则为创建窗口的线程被用来给属于自己窗口的消息排队,如果一个线程停止了(调用一个sleep)那么发送到该线程的窗口的消息将不被处理。 DWORD MyThreadStartProc(LPVOID hInstance){ HWND hWnd; MSG msg; hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, (HINSTANCE)hInstance, NULL); ShowWindow(hWnd, SW_SHOWNORMAL); UpdateWindow(hWnd); // Main message loop: while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return 0;// thread functioned correctly }   消息 放在消息队列里的消息通过整数ID标识,并带有两个参数,wParam和lParam,消息的发送者可以是系统的鼠标键盘驱动、窗口控件的实现代码、你的程序代码或ActiveX控件,消息的接收者可以一般是发生该消息的窗口的窗口过程,也有可能是它的父窗口的窗口过程。   消息ID Windows的平台SDK定义了一组和平台窗口相关的消息,它们的名字都是WM_XXX的形式,这些消息对普通窗口和标准/通用控件(它们是基于普通窗口的)都有效。所以当鼠标在一个窗口内移动时,它的窗口过程就会收到一个WM_MOUSEMOVE消息,几乎所有的WM_消息都是这样,只有三个例外:WM_COMMAND,WM_NOTIFY和WM_CTLCOLORXXX这三个消息是发送到发生动作的窗口的父窗口的。 程序可以发送自定义的消息来实现程序通讯,自定义消息的值应该和WM_USER相加来形成一个有效的消息ID。接收消息的窗口过程应该把消息值减去WM_USER进行处理。 要像关闭一个程序,发送WM_QUIT消息。   标准控件和通用控件 Windows随核心一起提供了一套标准控件,它们总是可用的。包括按钮,组合框,编辑框,列表框,滚动条和静态组件。 注意,富文本框(Rich-Edit)控件是标准控件,不是通用控件,分三个版本发布,每个都比前一个支持更多的富文本特性。(省略部分对Rich-Edit的版本介绍) Windows通用提供了更多的高级控件,叫做通用控件,只要程序调用了InitCommonControls/InitCommonControlsEx API并且系统安装了相应版本的Comctl32.dll就可以使用它们。 “经典的”Windows95提供了通用控件的核心,比如Treeview,listview和property page。后来随IE4(4.71)发布的Comctl32.dll提供了更多的控件,比如IPAddress,DateTime Picker,MonthCalendar。IE5没有添加通用控件,但是为这些控件增加了一些新的消息,比如Treeview的TVM_GETLINECOLOR   获取和设置信息 系统没提供真正用于操作这些控件的API,而是让程序通过发送消息来操作它们。比如给一个Edit发送EM_SETLIMITTEXT消息,并在wParam中指定具体的限制;发送EM_GETLIMITTEXT就可以从SendMessage的返回值得到这个限制的数目。   命令和通知 程序在使用标准/通用控件时往往想在控件收到特定小时时得到通知,我们已经看到了,可以通过子类/超类的方法让程序代码比默认的窗口过程更早地得到消息,处理特定的消息,传递不感兴趣的给默认窗口过程。对于特定的情况这很好,但是如果你的程序有很多个对话框,每个对话框上又有许多控件时,你的工作量就大得难以接受了。我们需要更简单地使用这些控件的方法。如果一个对话框(控件的父窗口)能得知它里面的控件的一些重要的动作,比如在edit box里输入了一个字符等。幸运的是,操作系统就是这样设计的,它发送WM_COMMAND消息和WM_NOTIFY消息给父窗口,并在参数里指明子窗口上发生了什么。 对于每个标准/通用控件,系统指定了当一些特定的动作发生是,父窗口得到通知。控件自身可能会收到WM_LBUTTONDOWN消息,但是它发送一个WM_COMMAND消息给父窗口,包含了一个BN_CLICKED通知消息在里面。标准控件和Animation通用控件使用WM_COMMAND,所有其他的都使用WM_NOTIFY。   窗口位于哪个层次 对Windows编程的一个好建议是心中要时刻清晰地知道你正在处理的窗口层次。通常的情况是我们无法正确估计窗口的数量,并且应该发送给父窗口的消息却发送到了另外一个窗口。这也许发生在ActiveX控件上,这些控件可以被设计为“无窗口的”,控件本身并没有自己的窗口而是对话框中工作的。当这个控件在一个编程环境中“安家”时,它被窗口类所封装,这个窗口类管理了一个“地址(site)”窗口,这个地址窗口在窗口层次中位于对话框窗口之下(它是对话框窗口的子窗口)和控件窗口(它是控件窗口的父窗口)之上 充分利用Spy++工具可以帮你理清窗口的层次关系,减少出错的机会和调试的时间。   线程和窗口 线程和窗口在很多的层次上存在交互,清楚理解这种交互可以避免许多潜在的问题并优化性能。 在Win32上,只有唯一一种线程,它执行threadproc中的用户界面或非界面程序代码。在操作系统的观点上没有“用户界面线程”这种东西,用户界面线程和工作者线程的差别只存在于程序层次上,因为你是程序的开发者,你决定了那些和UI有关的代码该在一个线程里,另外的UI无关的代码应该在另一个线程中调用。这你你做出的决定,操作系统并不关心这个。(这个观点同样适用于COM线程。没有“COM线程”这东西,它仅仅是一个恰巧执行了COM的Win32线程)在Win32里你可以在任何线程中执行UI调用,你可能是出于设计的考虑把UI调用集中到某些线程中。为了做出更好的设计,必须深入了解windows操作在线程上下文中是如何执行的。 下面是三本讲线程的经典书籍: Richter的“Programming Applications for Microsoft Windows” – ISBN: 1-57231-996-8 第26章; Berveridge’s/Wiener的“Multithreading Applications in Win32” ISBN: 0-201-44234-5, 第11章; Cohen’s/Woodring的“Win32 Multithreaded Programming” ISBN: 1-56592-296-4, 第12章.   谁拥有这个对象? 一个程序可以创建许多对象,比如窗口,pen,brush等等。一个好的程序员有义务在不再需要这些对象时删除它们。及时没有这么做,核心也会在线程终结时或进程终结时介入,删除那些依然存在的对象。Window对象(和Hook对象)是被创建它们的线程所拥有,当线程退出时,该线程创建的每个窗口都会被关闭。其他的对象(pen,brush,region,DC,bitmap,font等等)是被进程拥有,不管它们是由哪个线程创建,它们都将存活直到被显式地删除或进程(非线程)终结。   线程和窗口消息队列 窗口/线程交互的一条黄金定律是创建窗口的线程(通过CreateWindow[Ex])也是处理该窗口消息的线程。其他大部分的Win32窗口/线程交互都遵循此定律。 为了理解此定律的含义,我们必须先回答几个问题。如果一个线程将会接受窗口消息,那么它有没有一个消息队列?如果有,是如何创建的?消息是如何放置到这个队列中的?如果一个线程有消息队列,它就需要一个消息泵来从队列中提取并处理消息,这又是怎么实现的?当一个线程死亡了,与之相关的窗口也会死亡,消息也就不会再发送给它们了,这怎么处理?如果一个拥有消息泵的线程同样等待一个系统核心对象(比如Mutex)会怎么样? 一些线程需要消息队列,一些不需要。还有一些线程在窗口运行中从来就没有用到,为它们分配内存构建一个从来都不用的消息队列是不划算的。隐藏,当OS创建一个线程是,并不会自动为它创建消息队列。这个线程一调用Win32窗口API,系统就会为它创建消息队列,这对程序代码来说是透明的。消息队列一旦存在,就一直存活到线程终结为止。 程序需要知道哪个线程有消息队列,以便为它添加熟悉的GetMessage/DispatchMessage消息泵。程序知道这个,因为消息队列仅仅在一个线程调用了窗口API后才被创建。因此在什么地方添加消息泵是设计者的问题。   发送(Send)消息和投递(Post)消息 导致窗口过程被调用的两种主要技术是发送消息和投递消息。发送是这么做的: LRESULT SendMessage( //消息处理后的返回值 HWND hWnd, // 接受消息的窗口 UINT Msg, // 发送的消息 WPARAM wParam, //消息参数 LPARAM lParam // 消息参数 }; 你可以认为Send是同步的,调用线程会阻塞知道消息真的被处理了,下一行代码才会被执行。hWnd参数指定了需要执行的窗口过程所在的线程。如果这个线程就是调用SendMessage的线程,窗口过程直接-没有线程上下文切换,消息也不放到消息队列里-被执行。如果接收消息的窗口在其他线程,一个消息被放置在那个线程的消息队列中,接下来的某个时候会被处理,并返回执行结果。然后调用SendMessage的线程“苏醒”了,继续执行。调用SendMessage从一个线程发送消息到另一个线程中的窗口是可行的,但是主要的问题是如果那个线程没有正常地处理消息泵,第一个线程将被阻塞。 投递可以用一下三个函数之一完成: BOOL PostMessage( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); 这个函数把消息放置到指定的窗口所被创建的线程的消息队列中。 BOOL PostThreadMessage( DWORD idThread, UINT Msg, WPARAM wParam, LPARAM lParam ); 这个函数把消息放置到指定的窗口所被创建的线程的消息队列中。接收时窗口句柄可能为0。 VOID PostQuitMessage( // no return int nExitCode // Exit-code ); 这个函数把一个WM_QUIT消息放置到调用该函数的线程的消息队列中。过些时候线程接收到WM_QUIT小时时应该正常地退出。 投递是异步的,消息被投递到队列,函数立即返回,不等待。消息可能被正确处理也可能不被。这个函数通常被用来实现线程间不需要同步的通讯。接收线程在适当的时候会从队列中取出这个消息并按顺序处理。 需要注意的一点是如果消息队列不存在会怎么样?调用Post的线程会在接收线程中创建吗?不!消息队列只会在该线程调用了任何窗口API是在自身中被创建。这点在WTL多线程SDI程序中是非常主要的,我们会在WTL AppWizard一章详细讨论。   线程和内核对象 GetMessage API是一个阻塞的调用,等待消息到达队列。WaitForSingleObject 或 WaitForMultipleObject 也是阻塞调用,等到一个或多个内核对象被通知(signaled)。如果不但要等到核心对象也要同时等待消息到达该怎么办?可以用MsgWaitForMultipleObjects和 MsgWaitForMultipleObjectsEx。 DWORD MsgWaitForMultipleObjects( DWORD nCount, // 句柄个数 CONST HANDLE pHandles, //句柄数组 BOOL fWaitAll, // 是否等待全部 DWORD dwMilliseconds, // 超时 DWORD dwWakeMask // 检测什么类型的输入 ); DWORD MsgWaitForMultipleObjectsEx( DWORD nCount, CONST HANDLE pHandles, DWORD dwMilliseconds, DWORD dwWakeMask, DWORD dwFlags //其他标志 ); 它们将等到若干个内核对象并检测消息到达,并有超时设置。 第4章-WTL快速之旅 目标 该章的目标为 介绍WTL中的模板(templates)和类(classes)
/
本文档为【WTL开发者指南】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
热门搜索

历史搜索

    清空历史搜索