第二章 一个最简单窗口程序的转型
我知道,可能会有很多朋友对上一章的“Hello, World!”ATL版不以为然,因为它并不能算是什么ATL程序——毕竟它只不过是有了个CComModule而已。不过不管怎样我还是要说,它几乎仍然拥有了一个ATL GUI程序的所有组成部分:入口、初始化、程序体、卸载……
“等等!”也许你会突然打断我,“——还有注册窗口类、消息循环呢?”
当然,对于一个完整的GUI程序来讲,这也是必要的。
貌似废话
不清楚你是否已经为本章的内容做好了准备,因为下面我们就要动真格的了。不过考虑到本书的读者群中可能会存在着相当一部分了解MFC却对Win32 GUI的基本原理和流程不甚熟悉的朋友,所以李马特别为你们准备了这一节的内容。SDK的粉丝们可以跳过这一节,如果你们觉得李马讲的有些拖沓冗长的话。
那么,我还是先以一个标准的Win32 SDK程序开始:
////////////////////////////////////////////////////////////////////////// //ATL的GUI程序设计配套源代码 //第二章一个最简单窗口程序的转型 //工程名称:HelloSDK //作者:李马 //http://www.titilima.cn //////////////////////////////////////////////////////////////////////////
#include<windows.h> #include<tchar.h>
LRESULTCALLBACKHelloWndProc(HWNDhWnd,UINTuMsg,WPARAMwParam,LPARAMlParam) { switch(uMsg) { caseWM_DESTROY: { PostQuitMessage(0); } break; caseWM_PAINT: { HDChdc; PAINTSTRUCTps;
hdc=BeginPaint(hWnd,&ps); DrawText(hdc,_T("Hello,SDK!"),-1,&ps.rcPaint,DT_CENTER|DT_VCENTER|DT_SINGLELINE); EndPaint(hWnd,&ps); } break; default: returnDefWindowProc(hWnd,uMsg,wParam,lParam); } return0; }
BOOLInitApplication(HINSTANCEhInstance) { WNDCLASSwc; wc.cbClsExtra=0; wc.cbWndExtra=0; wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH); wc.hCursor=LoadCursor(NULL,IDC_ARROW); wc.hIcon=LoadIcon(NULL,IDI_APPLICATION); wc.hInstance=hInstance; wc.lpfnWndProc=HelloWndProc; wc.lpszClassName=_T("HelloSDK"); wc.lpszMenuName=NULL; wc.style=CS_HREDRAW|CS_VREDRAW;
returnRegisterClass(&wc); }
intWINAPI_tWinMain(HINSTANCEhInstance,HINSTANCEhPrevInstance,LPTSTRlpCmdLine,intnShowCmd) { //注册窗口类 InitApplication(hInstance);
//创建窗口 HWNDhWnd=CreateWindow(_T("HelloSDK"),_T("HelloSDK"),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInstance,NULL); ShowWindow(hWnd,nShowCmd); UpdateWindow(hWnd);
//消息循环 MSGmsg; while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); }
returnmsg.wParam; } |
不知道你是否会觉得这段代码有些冗长?事实上,这个程序已经体现了Win32 GUI程序运行的所有流程(请注意,我并不会对这些代码进行详细的解释,因为我已经假设你已经了解了这些代码具体行为的必要细节。如果不是这样的话,请参考相关的书籍或者MSDN):
- 注册窗口类的部分。在这个程序中,InitApplication函数完成了这一工作。窗口类的概念类似于OO(面向对象)中的类,所有你在Windows中能看到的窗口都是某个特定窗口类的一份实例。但是,窗口类并非任何一种OOP语言中的类——它所包括的并不是通称的属性和方法(在C++中称作成员变量和成员函数),而是属性和响应。这个区别可能会使你感到费解,我会在下一章中为你详细介绍——因为ATL中对窗口的封装类将这一点体现得十分淋漓尽致。
- 创建窗口的部分。在通常的SDK代码里,这些代码被封装在一个名为InitInstance的函数中。这段代码所做的工作一般是创建窗口并将其显示出来。
- 消息循环。Windows是一个基于消息机制的操作系统,各个窗口之间的通信也主要是靠Windows消息来完成的。而程序中的消息循环也就是将本程序UI线程中的消息队列中提取各种消息,进行处理(如果有必要的话)之后分发给各个消息的属主窗口(或者说是目标窗口)。
在这里需要指出的是,HelloWndProc是我们自己定义的一个函数,我们需要用它来控制我们对特定窗口消息的特定响应。我们只需要在注册窗口类之前,将这个函数的地址(也就是函数名)赋值给WNDCLASS::lpfnWndProc成员就可以了。这个函数我们自己不需要进行调用,它的调用是当我们的窗口收到窗口消息后,由Windows完成的。在这个回调函数中,我们的处理是这样的:
- WM_DESTROY。在窗口被销毁的时候,窗口会收到此消息。在这里,我们会调用PostQuitMessage,用以向当前UI线程的消息队列之中发送一条WM_QUIT消息,GetMessage在收到这条消息后,会返回FALSE,也就结束了消息循环,WinMain也就结束了。
- WM_PAINT。在窗口需要绘制的时候,窗口会收到此消息。在这里我们只是简单的在窗口的中间绘制了一行文字“Hello, SDK!”。
- 其它消息。这些消息都是我们不关心的,所以我们将其交由系统默认的窗口过程DefWindowProc来处理。
这段代码貌似冗长,但实际上还是很有条理的,你可以根据它以及我以上的解说来对照这个程序的ATL版本。
ATL等同品
在写作这本书的时候,我总是希望我每次都能够能使用让你不太陌生的代码来循序渐进地引导你。考虑再三,对于“Hello, ATL!”的这个程序,我决定先把它的WinMain展现给你:
intWINAPI_tWinMain(HINSTANCEhInstance,HINSTANCEhPrevInstance,LPTSTRlpCmdLine,intnShowCmd) { _Module.Init(NULL,hInstance);
//创建窗口 CHelloATLWndwnd; wnd.Create(NULL,CHelloATLWnd::rcDefault,_T("HelloATL")); wnd.ShowWindow(nShowCmd); wnd.UpdateWindow();
//消息循环 MSGmsg; while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); }
_Module.Term(); returnmsg.wParam; } |
OK,上一章介绍过的_Module又出现在你的眼前了——不过还是没有什么特别的变化,仍然是那熟悉的Init和Term。而且,正如“山哟还是那座山”一样,消息循环哟也仍然是那个消息循环。当然,你肯定也发现了那寥寥的变化:CHelloATLWnd是什么?在我将它的代码展现给你之前,你可能会做出这样的猜想:
- 这是一个C++类,它对Win32窗口类进行了封装。
- 这个类封装了大多数窗口操作的API函数,诸如CreateWindow、ShowWindow、UpdateWindow。
- 窗口类的注册可能也是在这个C++类中完成的。
好,打住,这就够了。让我们来撩开CHelloATLWnd那貌似神秘的面纱吧,赶紧着。
classCHelloATLWnd:publicCWindowImpl<CHelloATLWnd,CWindow,CWinTraits<WS_OVERLAPPEDWINDOW>> { public: CHelloATLWnd() { CWndClassInfo&wci=GetWndClassInfo(); wci.m_bSystemCursor=TRUE; wci.m_lpszCursorID=IDC_ARROW; wci.m_wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH); wci.m_wc.hIcon=LoadIcon(NULL,IDI_APPLICATION); } public: DECLARE_WND_CLASS(_T("HelloATL")) public: BEGIN_MSG_MAP(CHelloATLWnd) MESSAGE_HANDLER(WM_DESTROY,OnDestroy) MESSAGE_HANDLER(WM_PAINT,OnPaint) END_MSG_MAP() public: LRESULTOnDestroy(UINTuMsg,WPARAMwParam,LPARAMlParam,BOOL&hHandled) { ::PostQuitMessage(0); return0; } LRESULTOnPaint(UINTuMsg,WPARAMwParam,LPARAMlParam,BOOL&hHandled) { HDChdc; PAINTSTRUCTps;
hdc=BeginPaint(&ps); DrawText(hdc,_T("Hello,ATL!"),-1,&ps.rcPaint,DT_CENTER|DT_VCENTER|DT_SINGLELINE); EndPaint(&ps); return0; } }; |
猜想,还是猜想!
请允许我在本章中不为你解释这个类的任何具体细节,取而代之的是继续的猜想。因为,这个类中需要解释的东西太多了,以至于我必须为它单独开辟一章。
- 窗口类的注册是由这个C++类的构造函数与DECLARE_WND_CLASS宏一起完成的。
- 对于BEGIN_MSG_MAP与END_MSG_MAP这一部分,想必使用过MFC的朋友们应该更容易理解。是的,这一对宏可以算作ATL的消息映射,在其中由MESSAGE_HANDLER作为消息分流器,将各种窗口消息分配给各个处理函数。
- 创建窗口时指定的样式貌似和模板参数CWinTraits有关。
当然,除了这些猜想之外,你可能还会同时存在以下疑问:
- CWindowImpl、CWindow、CWinTraits究竟是什么?
- 窗口类是在何时注册的?
- 消息分流器是如何实现的?
也许你还会有更多的疑问,那么就让我一并将它们留到下一章再解决吧。如果你实在等不及的话,atlwin.h的代码也会告诉你一切的。
补叙CComModule
由于这本书主要针对的是ATL 3.0/Visual C++ 6.0,所以我疏忽了对CComModule的研究。在此感谢老李老刀兄提出的一点,就是CComModule在ATL 7.0中已经不建议使用了。于是我将MSDN中的相关章节摘抄下来,权作借花献佛之用。
CComModule 替换类
ATL 的早期版本使用 CComModule。在 ATL 7.0 中,CComModule 功能被若干个类所取代:
- CAtlBaseModule 包含大多数使用 ATL 的应用程序所需的信息。包含模块和资源实例的 HINSTANCE。
- CAtlComModule 包含 ATL 中的 COM 类所需的信息。
- CAtlWinModule 包含 ATL 中的窗口化类所需的信息。
- CAtlDebugInterfacesModule 包含接口调试支持。
- CAtlModule 下列 CAtlModule 派生的类被自定义为包含特定应用程序类型中所需的信息。这些类中的大部分成员都可以被重写:
CAtlDllModuleT 在 DLL 应用程序中使用。为标准导出提供代码。
CAtlExeModuleT 在 EXE 应用程序中使用。提供 EXE 中所需的代码。
CAtlServiceModuleT 为创建 Windows NT 和 Windows 2000 服务提供支持。
CComModule 仍然可用以便向后兼容。
分布 CComModule 功能的原因
由于以下原因,CComModule 的功能分布到了几个新类中:
- 使 CComModule 中的功能呈粒状分割。
对 COM、窗口化、接口调试和应用程序特定的(DLL 或 EXE)功能的支持现在在不同的类中。
- 自动为这些模块的每一个声明全局实例。
所需模块类的全局实例链接到项目中。
- 消除了调用 Init 和 Term 方法的必要性。
Init 和 Term 方法已移动到模块类的构造函数和析构函数中;不再需要调用 Init 和 Term。
不过,出于代码的兼容性以及WTL的内容考虑,本系列后续文章仍然将使用ATL 3.0中的CComModule。
分享到:
相关推荐
MFC开发人员的WTL编程简介。
ATL 测试程序 ,简单方便,比较实用。
ATL的发明人Jim Springfield亲自作序推荐 四位顶尖的Windows编程专家倾力合作,绝对经典再现 COM、ATL开发人员的必备宝典 深入分析ATL实现COM内幕细节,展示COM应用中的各类漂亮技巧 本书主要介绍了ATL技术的...
适合人群: 对森林、地形、冰川等地物高度研究的人群 如何将星载lidar ICESAT-2的atl8和atl3结合,综合利用地理定位信息与地面高度信息
这个程序是基于VC的 ATL的简单程序,程序生成一个DLL的activex空件, 只包含一个函数 add 可以用下面代码实现网页调用(当然要先注册控件了) <TITLE>ATL 3.0 test page for object MyFunAdd ...
PDF ATL程序设计 500页 比较清楚 可以下载学习ATL
atl开发指南 atl开发指南 atl开发指南 atl开发指南 atl开发指南
ATL服务编程入门参考 启动 停止 安装 卸载
中文名: 深入解析ATL(第2版) 原名: ATL Internals, 2nd Edition Working with ATL 8 别名: ATL 作者: (美)塔瓦瑞斯译者: 赖仪灵 曹雨田 资源格式: PDF 版本: 扫描版 出版社: 电子工业出版社书号: 9787121049859...
ATL编写的Windows服务小程序 虽然只是个入门级程序 但是掌握入门知识的感觉 仍然是老帅了。。。
中文版 清晰 pdf,经典ATL又一升级力作,...因为不能大于60M,所以只能分开为2部分了。 原书名: ATL Internals: Working with ATL 8 (2nd Edition) 作者: (美)Chris Tavares Kirk Fertitta Brent Rector Chris Sells
ATL技术内幕ATL技术内幕ATL技术ATL技术内幕内幕
使用ATL设计组件,写了例子和测试例子,简单明了。
深入解析ATL(第2版)PDF 高清中文版 ATL8.0 VS2005 part5
第一部分 - ATL 中的 GUI 类 • 下载示例工程 - 45.5 KB 本章内容 • README.TXT • 本系列介绍 • 第一部分介绍 • ATL 背景知识 o ATL 和 WTL 的历史 o ATL 风格的模板 • ATL 窗口类 • 定义窗口实现 o 填充消息...
ATL开发指南。我自己放在这里备份用。 本书是介绍使用ATL进行软件开发的参考用书。全书分为十三章:第一章...本书的主要对象是程序设计或开发人员,同时也可以作为大专院校计算机专业师生和计算机爱好者的参考资料。
python实现ICESat-2 ATL08转shapefile
collection.zip:包含VC Atl开发的集合的源代码(组件程序和测试程序) enum.zip:包含VC Atl开发的枚举器的源代码(组件程序和测试程序) event.zip:包含VC Atl开发的事件的源代码(组件程序和测试程序) win....
在ATL服务器DLL嵌入MFC GUI接口
刚开始学习ATL,做了一个简单的小程序练习练习,还有很多不详尽之处