王朝网络
分享
 
 
 

ATL的GUI程序设计(2)

王朝other·作者佚名  2006-05-27
宽屏版  字体: |||超大  

第二章 一个最简单窗口程序的转型

我知道,可能会有很多朋友对上一章的“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>

LRESULT CALLBACK HelloWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )

{

switch ( uMsg )

{

case WM_DESTROY:

{

PostQuitMessage( 0 );

}

break;

case WM_PAINT:

{

HDC hdc;

PAINTSTRUCT ps;

hdc = BeginPaint( hWnd, &ps );

DrawText( hdc, _T("Hello, SDK!"), -1, &ps.rcPaint, DT_CENTER | DT_VCENTER | DT_SINGLELINE );

EndPaint( hWnd, &ps );

}

break;

default:

return DefWindowProc( hWnd, uMsg, wParam, lParam );

}

return 0;

}

BOOL InitApplication( HINSTANCE hInstance )

{

WNDCLASS wc;

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;

return RegisterClass( &wc );

}

int WINAPI _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nShowCmd )

{

// 注册窗口类

InitApplication( hInstance );

// 创建窗口

HWND hWnd = CreateWindow( _T("HelloSDK"), _T("Hello SDK"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,

CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL );

ShowWindow( hWnd, nShowCmd );

UpdateWindow( hWnd );

// 消息循环

MSG msg;

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

{

TranslateMessage( &msg );

DispatchMessage( &msg );

}

return msg.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展现给你:

int WINAPI _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nShowCmd )

{

_Module.Init( NULL, hInstance );

// 创建窗口

CHelloATLWnd wnd;

wnd.Create( NULL, CHelloATLWnd::rcDefault, _T("Hello ATL") );

wnd.ShowWindow( nShowCmd );

wnd.UpdateWindow();

// 消息循环

MSG msg;

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

{

TranslateMessage( &msg );

DispatchMessage( &msg );

}

_Module.Term();

return msg.wParam;

}

OK,上一章介绍过的_Module又出现在你的眼前了——不过还是没有什么特别的变化,仍然是那熟悉的Init和Term。而且,正如“山哟还是那座山”一样,消息循环哟也仍然是那个消息循环。当然,你肯定也发现了那寥寥的变化:CHelloATLWnd是什么?在我将它的代码展现给你之前,你可能会做出这样的猜想:

这是一个C++类,它对Win32窗口类进行了封装。

这个类封装了大多数窗口操作的API函数,诸如CreateWindow、ShowWindow、UpdateWindow。

窗口类的注册可能也是在这个C++类中完成的。

好,打住,这就够了。让我们来撩开CHelloATLWnd那貌似神秘的面纱吧,赶紧着。

class CHelloATLWnd : public CWindowImpl< 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:

LRESULT OnDestroy( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& hHandled )

{

::PostQuitMessage( 0 );

return 0;

}

LRESULT OnPaint( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& hHandled )

{

HDC hdc;

PAINTSTRUCT ps;

hdc = BeginPaint( &ps );

DrawText( hdc, _T("Hello, ATL!"), -1, &ps.rcPaint, DT_CENTER | DT_VCENTER | DT_SINGLELINE );

EndPaint( &ps );

return 0;

}

};

猜想,还是猜想!

请允许我在本章中不为你解释这个类的任何具体细节,取而代之的是继续的猜想。因为,这个类中需要解释的东西太多了,以至于我必须为它单独开辟一章。

窗口类的注册是由这个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。

点这里下载本章配套代码

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
>>返回首页<<
推荐阅读
 
 
频道精选
 
静静地坐在废墟上,四周的荒凉一望无际,忽然觉得,凄凉也很美
© 2005- 王朝网络 版权所有