您的位置:首页 > 其它

Learning WTL8.0 Part-1 Win32 vs. ATL Windows Programming

2007-06-20 16:49 337 查看
学习WTL可以有多种方式,当然如果有COM和ATL的知识背景最好不过,如果你有MFC编程背景却最为糟糕,除非你对MFC无所不知、无所不能(如果你不是MFC的ORACLE,那么最好忘却它)

Learning WTL8.0 Part-1
Learning WTL8.0 Part-1

Win32 vs. ATL
Windows Programming

目录

目录.. 2
概要.. 3
1. “Hello World!” in Win32. 3
1.1创建一个Win32 Project3
1.1.1 选择Win32 Project3
1.1.2 应用程序设置.. 4
1.1.3 编译器选项设置.. 5
1.2 Win32程序的基本结构.. 6
1.2.1 WinMain. 6
1.2.1 Register windows class. 7
1.2.3 Initialize application. 7
1.2.4 WndProc8
1.3 Build与程序输出.. 9
2. “Hello World!” in ATL. 9
2.1 修改stdafx.h. 9
2.2 添加CWellcomeWindow.h. 10
2.3 修改win32.cpp. 10
2.4 Build与程序输出.. 11
3. ATL vs. Win32. 12
3.1 编程模型.. 12
3.1.1 Win32基础构造块和主要流程.. 13
3.1.2 ATL基础构造块和主要流程.. 13
3.2 Build过程和程序输出.. 13
4. ATL Windows编程模型.. 14
4.1 RegisterClass在哪?. 14
4.1.1 DECLARE_WND_CLASS(NULL)宏定义.. 14
4.1.2 CWndClassInfo结构(_ATL_WNDCLASSINFOW)定义.. 14
4.1.3 ATL::CWindowImpl::Create定义.. 15
4.2 WndProc在哪?.. 15
4.2.1 Message-Map. 15
5. Tips. 16
5.1 UNREFERENCED_PARAMETER Macro. 16
5.2 CXxx : public CYyy<CXxx>. 17
5.3 throw()18
总结.. 18

概要

学习WTL可以有多种方式,当然如果有COM和ATL的知识背景最好不过,如果你有MFC编程背景却最为糟糕,除非你对MFC无所不知、无所不能: -)(如果你不是MFC的ORACLE,那么最好忘却它)

本系列打算从Win32和ATL入手,来学习WTL,情况理想的话的可以做到一举四得: Win32、ATL、WTL、C++(OO和泛型编程)

WTL的文档相对较少,且有些文档多是针对WTL3.0和WTL7.1,相对于最新的WTL8.0有些人老珠黄,也些范式已不太常用了,这也是促使写本系列的最初想法,帮助自己也帮助和我有同样需要的人,如果可以的话。

本系列中所用的工具和库为VS.NET Team Edition 2005,WTL80-6137;如果你没有VS2005,可以到M$站点http://msdn.microsoft.com/vstudio/express/ 上去下一个C++ Express;另外,假定你已安装好所有必备环境包括WTL了。

Part-1主要是从Win32和ATL着手,来讲解Windows编程的基本概念,有Win32和ATL基础的可以略过。: -)

建议:如果有条件的话最好跟随 1. “Hello World!” in Win322. “Hello World!” in ATL ,做完2个显示”Hello World!”的小程序,稍后将在 3. ATL vs. Win32 作进一步的讲解。

1. “Hello World!” in Win32

1.1创建一个Win32 Project

当然创建过程相当简单,主要目的是为了了解一下Win32程序的基本结构并为写第一个ATL程序作准备。

[align=left]在WndProc中:[/align]
[align=left] case WM_PAINT:[/align]
[align=left] hdc = BeginPaint(hWnd, &ps);[/align]
[align=left] // TODO: Add any drawing code here...[/align]
[align=left] EndPaint(hWnd, &ps);[/align]
[align=left] break;[/align]
[align=left]// TODO:标签下添加TextOut(hdc, 0, 0, _T("Hello world!"), 12);语句。[/align]

有3点需要注意的,请按图示:

1.1.1 选择Win32 Project
Figure-1.1-1



1.1.2 应用程序设置

Figure-1.1-2



采用默认设置。
1.1.3 编译器选项设置

Figure-1.1-3



[align=left]将C/C++ -> Advanced -> Compile As 设为Compile as C Code(/TC), 这点很容易被忽视,如果你要写纯血统M$ C而又不想过多的关注C/C++文件扩展名的话;如果,你设置了/TC编译器选项,请将 [/align]
[align=left]UNREFERENCED_PARAMETER(hPrevInstance);[/align]
[align=left] UNREFERENCED_PARAMETER(lpCmdLine);[/align]
[align=left]2句代码注释(commenting out)掉,关于UNREFERENCED_PARAMETER的使用将在5.1 UNREFERENCED_PARAMETER Macro 中谈到。[/align]

1.2 Win32程序的基本结构
VS2005向导产生的Win32 Project的块结构(block)和VS2003已有较大的改进了: -)。

以下代码均为了更清楚地说明主要问题而剔除了无关紧要的部分。
1.2.1 WinMain

[align=left]int APIENTRY _tWinMain(HINSTANCE hInstance,[/align]
[align=left] HINSTANCE hPrevInstance,[/align]
[align=left] LPTSTR lpCmdLine,[/align]
[align=left] int nCmdShow)[/align]
[align=left]{[/align]
[align=left] // TODO: Place code here.[/align]
[align=left] MSG msg;[/align]
[align=left] [/align]
[align=left] MyRegisterClass(hInstance);[/align]
[align=left] [/align]
[align=left] // Perform application initialization:[/align]
[align=left] if (!InitInstance (hInstance, nCmdShow))[/align]
[align=left] {[/align]
[align=left] return FALSE;[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] // Main message loop:[/align]
[align=left] while (GetMessage(&msg, NULL, 0, 0))[/align]
[align=left] {[/align]
[align=left] TranslateMessage(&msg);[/align]
[align=left] DispatchMessage(&msg);[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] return (int) msg.wParam;[/align]
[align=left]}[/align]

以上代码结构主要由3个块构成:Register windows class、Initialize application和Main message loop 。

1.2.1 Register windows class

[align=left]ATOM MyRegisterClass(HINSTANCE hInstance)[/align]
[align=left]{[/align]
[align=left] WNDCLASSEX wcex;[/align]
[align=left] [/align]
[align=left] wcex.cbSize = sizeof(WNDCLASSEX);[/align]
[align=left] [/align]
[align=left] wcex.style = CS_HREDRAW | CS_VREDRAW;[/align]
[align=left] wcex.lpfnWndProc = WndProc;[/align]
[align=left] wcex.cbClsExtra = 0;[/align]
[align=left] wcex.cbWndExtra = 0;[/align]
[align=left] wcex.hInstance = hInstance;[/align]
[align=left] wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32));[/align]
[align=left] wcex.hCursor = LoadCursor(NULL, IDC_ARROW);[/align]
[align=left] wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);[/align]
[align=left] wcex.lpszMenuName = MAKEINTRESOURCE(IDC_WIN32);[/align]
[align=left] wcex.lpszClassName = szWindowClass;[/align]
[align=left] wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));[/align]
[align=left] [/align]
[align=left] return RegisterClassEx(&wcex);[/align]
}

1.2.3 Initialize application
主要为CreateWindow、ShowWindow 。

[align=left]BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)[/align]
[align=left]{[/align]
[align=left] HWND hWnd;[/align]
[align=left] [/align]
[align=left] hInst = hInstance; // Store instance handle in our global variable[/align]
[align=left] [/align]
[align=left] hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,[/align]
[align=left] CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);[/align]
[align=left] [/align]
[align=left] if (!hWnd)[/align]
[align=left] {[/align]
[align=left] return FALSE;[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] ShowWindow(hWnd, nCmdShow);[/align]
[align=left] [/align]
[align=left] return TRUE;[/align]
[align=left]}[/align]

1.2.4 WndProc
在这里,主要为处理WM_PAINT和WM_DESTROY消息

[align=left]LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)[/align]
[align=left]{[/align]
[align=left] int wmId, wmEvent;[/align]
[align=left] PAINTSTRUCT ps;[/align]
[align=left] HDC hdc;[/align]
[align=left] [/align]
[align=left] switch (message)[/align]
[align=left] {[/align]
[align=left] case WM_PAINT:[/align]
[align=left] hdc = BeginPaint(hWnd, &ps);[/align]
[align=left] // TODO: Add any drawing code here...[/align]
[align=left] TextOut(hdc, 0, 0, _T("Hello world!"), 12);[/align]
[align=left] EndPaint(hWnd, &ps);[/align]
[align=left] break;[/align]
[align=left] case WM_DESTROY:[/align]
[align=left] PostQuitMessage(0);[/align]
[align=left] break;[/align]
[align=left] default:[/align]
[align=left] return DefWindowProc(hWnd, message, wParam, lParam);[/align]
[align=left] }[/align]
[align=left] return 0;[/align]
}

1.3 Build与程序输出



2. “Hello World!” in ATL

为了避免创建ATL COM EXE SERVER的开销和更好地说明Win32与ATL的内在关系,本例程序基于 1.1创建一个Win32 Project的程序创建。

对于ATL Windows编程陌生的,于此处不必过于在意,稍后在3. 对Win32和ATL的初步观察和比较会做一定的讲解。

2.1 修改stdafx.h
1.1创建的Win32代码中移除或注释掉stdafx.h中的:

[align=left]// Windows Header Files:[/align]
[align=left]#include <windows.h>[/align]
[align=left] [/align]
[align=left]// C RunTime Header Files[/align]
[align=left]#include <stdlib.h>[/align]
[align=left]#include <malloc.h>[/align]
[align=left]#include <memory.h>[/align]
[align=left]#include <tchar.h>[/align]

[align=left] [/align]
[align=left]并在stdafx.h中添加如下的include语句:[/align]
[align=left] [/align]

[align=left]#include <atlbase.h>[/align]
[align=left]extern CComModule _Module;[/align]
[align=left]#include <atlcom.h>[/align]
[align=left]#include <atlwin.h>[/align]
[align=left]#include "CWellcomeWindow.h"[/align]

[align=left] [/align]
2.2 添加CWellcomeWindow.h

[align=left]#pragma once[/align]
[align=left]#include "stdafx.h"[/align]
[align=left] [/align]
[align=left]class CWellcomeWindow : public CWindowImpl<CWellcomeWindow, [/align]
[align=left] CWindow, CFrameWinTraits> {[/align]
[align=left]public:[/align]
[align=left] DECLARE_WND_CLASS(NULL);[/align]
[align=left] [/align]
[align=left] BEGIN_MSG_MAP(CWellcomeWindow)[/align]
[align=left] MESSAGE_HANDLER(WM_CREATE, OnCreate)[/align]
[align=left] MESSAGE_HANDLER(WM_PAINT, OnPaint)[/align]
[align=left] MESSAGE_HANDLER(WM_DESTROY, OnDestroy)[/align]
[align=left] END_MSG_MAP()[/align]
[align=left] [/align]
[align=left] LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)[/align]
[align=left] {[/align]
[align=left] HICON appIcon = LoadIcon(_Module.GetResourceInstance(), [/align]
[align=left] MAKEINTRESOURCE(IDI_LEARNINGWTLPART1_ATL));[/align]
[align=left] this->SetIcon(appIcon);[/align]
[align=left] [/align]
[align=left] return 0;[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] [/align]
[align=left] LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)[/align]
[align=left] {[/align]
[align=left] PAINTSTRUCT ps;[/align]
[align=left] CComBSTR wellcome(_T("Hello World!"));[/align]
[align=left] [/align]
[align=left] HDC hdc; // = this->GetDC();[/align]
[align=left] [/align]
[align=left] hdc = this->BeginPaint(&ps); // this->BeginPaint(&ps);[/align]
[align=left] TextOut(hdc, 0, 0, wellcome, wellcome.Length());[/align]
[align=left] this->EndPaint(&ps);[/align]
[align=left] //this->ReleaseDC(hdc);[/align]
[align=left] [/align]
[align=left] return 0;[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)[/align]
[align=left] {[/align]
[align=left] ::PostQuitMessage(0);[/align]
[align=left] [/align]
[align=left] return 0;[/align]
[align=left] }[/align]
};

2.3 修改win32.cpp
[align=left]保留WinMain方法声明和#include "stdafx.h"语句将其余代码删除或注释掉。添加_Module定义,修改WndMain方法体;完成后的代码如下:[/align]

[align=left]#include "stdafx.h"[/align]
[align=left]#include "LearningWTLPart1_ATL.h"[/align]
[align=left] [/align]
[align=left]CComModule _Module;[/align]
[align=left] [/align]
[align=left]int APIENTRY _tWinMain(HINSTANCE hInstance,[/align]
[align=left] HINSTANCE hPrevInstance,[/align]
[align=left] LPTSTR lpCmdLine,[/align]
[align=left] int nCmdShow)[/align]
[align=left]{[/align]
[align=left] UNREFERENCED_PARAMETER(hPrevInstance);[/align]
[align=left] UNREFERENCED_PARAMETER(lpCmdLine);[/align]
[align=left] [/align]
[align=left] _Module.Init(NULL, hInstance);[/align]
[align=left] [/align]
[align=left] CComBSTR appTitle;[/align]
[align=left] appTitle.LoadString(_Module.GetResourceInstance(), IDS_APP_TITLE);[/align]
[align=left] [/align]
[align=left] CWellcomeWindow wnd;[/align]
[align=left] wnd.Create(NULL, 0, appTitle);[/align]
[align=left] [/align]
[align=left] // Main message loop:[/align]
[align=left] MSG msg;[/align]
[align=left] while (GetMessage(&msg, NULL, 0, 0))[/align]
[align=left] {[/align]
[align=left] TranslateMessage(&msg);[/align]
[align=left] DispatchMessage(&msg);[/align]
[align=left] [/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] _Module.Term();[/align]
[align=left] [/align]
[align=left] return (int) msg.wParam;[/align]
[align=left]}[/align]

[align=left] [/align]
2.4 Build与程序输出
如果在1.1创建一个Win32 Project设置了编译器选项的,先将编译器选项设置恢复为默认值即/TP

程序输出如下:



以上的程序输出和 1.3 Build与程序输出对比,似乎缺点什么?!在Part-2再作交待。

3. ATL vs. Win32

首先,对Win32编程模型作了一个初步介绍,着重介绍ATL编程模型,这里并不存在对二者有孰优孰劣的假设;由于,对于作为M$ Windows程序员来说,或多或少、或直接或间接得都接触过Win32编程模型,故在本节中轻彼而重此。

其次,对Win32程序和ATL程序的Build过程和产生的可执行文件作一个初步的对照。
3.1 编程模型
Win32编程是整个Windows编程的基石,无论是ATL或WTL;因此,即使是ATL也必然包含Win32编程模型的基础构造块,只是ATL提供了轻量的基础构造块的封装(Encapsulation)。

Win32编程模型的基础构造块主要由Register windows class、Create window/Show window、Message loop和WndProc 4部分构成。

3.1.1 Win32基础构造块和主要流程



3.1.2 ATL基础构造块和主要流程



CComModule::Init & CComModule::Term : 是ATL关于COM Server的部分,在这里不必关注,在后续的WTL学习中还会相应地涉及到;

Main message loop : 在ATL中保留;

3.2 Build过程和程序输出
对Win32版与ATL版的Debug和Release分别作一个初步的对比;为说明一般性问题,在此不考虑编译器优化,因此,无论是Debug抑或Release均采用编译器默认设置。

[align=center][/align]
[align=center]Debug[/align]
[align=center]Release[/align]
[align=center]比重[/align]
Win32版
[align=right]100K[/align]
[align=right]68K[/align]
[align=center] [/align]
ATL版
[align=right]244K[/align]
[align=right]88K[/align]
[align=center]D244% | R129%[/align]
ATL版的Release,无论是物理文件大小还是运行时空间大小或运行效率都与Win32版的Release接近,且二者除系统库外,基本上不需要其它支持库(可能msvcrt.dll,gdi32.dll等)。

4. ATL Windows编程模型

参照3.1.1 Win32基础构造块和主要流程,来初步的了解ATL Windows编程模型(有关ATL Windows编程的文档极为“罕见“, 甚至与WTL相比 : -)) ;ATL Windows编程是WTL的基石,当然有必要在此费一番力气。
4.1 RegisterClass在哪?
1.2.1 Register windows class 中,MyRegisterClass函数要完成的工作:1. 初始化WNDCLASSEX;2. RegisterClassEx 。 而在ATL中,由CWellcomeWindow之由宏定义的成员函数(member function by macro defined)DECLARE_WND_CLASS(NULL)完成,其由wnd.Create(NULL, 0, appTitle) 调用(例如,在win32.cpp中)。

4.1.1 DECLARE_WND_CLASS(NULL)宏定义

[align=left]/////////////////////////////////////////////////////////////////////////////[/align]
[align=left]// CWndClassInfo - Manages Windows class information[/align]
[align=left] [/align]
[align=left]#define DECLARE_WND_CLASS(WndClassName) /[/align]
[align=left]static ATL::CWndClassInfo& GetWndClassInfo() /[/align]
[align=left]{ /[/align]
[align=left] static ATL::CWndClassInfo wc = /[/align]
[align=left] { /[/align]
[align=left] { sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS, StartWindowProc, /[/align]
[align=left] 0, 0, NULL, NULL, NULL, (HBRUSH)(COLOR_WINDOW + 1), NULL, WndClassName, NULL }, /[/align]
[align=left] NULL, NULL, IDC_ARROW, TRUE, 0, _T("") /[/align]
[align=left] }; /[/align]
[align=left] return wc; /[/align]
[align=left]}[/align]

CWndClassInfo为ATL内使用WNDCLASSEX结构的结构(C++),看看代码就应该有所了解了,关键在于 4.1.2 CWndClassInfo结构(_ATL_WNDCLASSINFOW)定义 的黑体部分(Bold)。

4.1.2 CWndClassInfo结构(_ATL_WNDCLASSINFOW)定义

[align=left]struct _ATL_WNDCLASSINFOW[/align]
[align=left]{[/align]
[align=left] WNDCLASSEXW m_wc;[/align]
[align=left] LPCWSTR m_lpszOrigName;[/align]
[align=left] WNDPROC pWndProc;[/align]
[align=left] LPCWSTR m_lpszCursorID;[/align]
[align=left] BOOL m_bSystemCursor;[/align]
[align=left] ATOM m_atom;[/align]
[align=left] WCHAR m_szAutoName[5+sizeof(void*)*CHAR_BIT];[/align]
[align=left] ATOM Register(WNDPROC* p)[/align]
[align=left] {[/align]
[align=left] return AtlWinModuleRegisterWndClassInfoW(&_AtlWinModule, &_AtlBaseModule, this, p);[/align]
[align=left] }[/align]
[align=left]};[/align]

4.1.3 ATL::CWindowImpl::Create定义

[align=left]HWND Create(HWND hWndParent, _U_RECT rect = NULL, LPCTSTR szWindowName = NULL,[/align]
[align=left] DWORD dwStyle = 0, DWORD dwExStyle = 0,[/align]
[align=left] _U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)[/align]
[align=left] {[/align]
[align=left] if (T::GetWndClassInfo().m_lpszOrigName == NULL)[/align]
[align=left] T::GetWndClassInfo().m_lpszOrigName = GetWndClassName();[/align]
[align=left] ATOM atom = T::GetWndClassInfo().Register(&m_pfnSuperWindowProc);[/align]
[align=left] [/align]
[align=left] dwStyle = T::GetWndStyle(dwStyle);[/align]
[align=left] dwExStyle = T::GetWndExStyle(dwExStyle);[/align]
[align=left] [/align]
[align=left] // set caption[/align]
[align=left] if (szWindowName == NULL)[/align]
[align=left] szWindowName = T::GetWndCaption();[/align]
[align=left] [/align]
[align=left] return CWindowImplBaseT< TBase, TWinTraits >::Create(hWndParent, rect, szWindowName,[/align]
[align=left] dwStyle, dwExStyle, MenuOrID, atom, lpCreateParam);[/align]
[align=left] }[/align]

真正完成CreateWindow。
4.2 WndProc在哪?
Main message loop在ATL予以了保留(例如,在win32.cpp中)。

WndProc由宏定义成员函数完成,参看 2.2 添加CWellcomeWindow.h可以看到如下代码:

[align=left] BEGIN_MSG_MAP(CWellcomeWindow)[/align]
[align=left] MESSAGE_HANDLER(WM_CREATE, OnCreate)[/align]
[align=left] MESSAGE_HANDLER(WM_PAINT, OnPaint)[/align]
[align=left] MESSAGE_HANDLER(WM_DESTROY, OnDestroy)[/align]
[align=left] END_MSG_MAP()[/align]

4.2.1 Message-Map

[align=left]#define BEGIN_MSG_MAP(theClass) /[/align]
[align=left]public: /[/align]
[align=left] BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID = 0) /[/align]
[align=left] { /[/align]
[align=left] BOOL bHandled = TRUE; /[/align]
[align=left] (hWnd); /[/align]
[align=left] (uMsg); /[/align]
[align=left] (wParam); /[/align]
[align=left] (lParam); /[/align]
[align=left] (lResult); /[/align]
[align=left] (bHandled); /[/align]
[align=left] switch(dwMsgMapID) /[/align]
[align=left] { /[/align]
[align=left] case 0:[/align]
[align=left] [/align]
[align=left]#define END_MSG_MAP() /[/align]
[align=left] break; /[/align]
[align=left] default: /[/align]
[align=left] ATLTRACE(ATL::atlTraceWindowing, 0, _T("Invalid message map ID (%i)/n"), dwMsgMapID); /[/align]
[align=left] ATLASSERT(FALSE); /[/align]
[align=left] break; /[/align]
[align=left] } /[/align]
[align=left] return FALSE; /[/align]
[align=left] }[/align]

你所定义的特定的消息处理将会置于case 0defaul之间,例如 2.2 添加CWellcomeWindow.h将会产生如下代码:

[align=left]BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID = 0) [/align]
[align=left] { [/align]
[align=left] BOOL bHandled = TRUE; [/align]
[align=left] (hWnd); [/align]
[align=left] (uMsg); [/align]
[align=left] (wParam); [/align]
[align=left] (lParam); [/align]
[align=left] (lResult); [/align]
[align=left] (bHandled); [/align]
[align=left] switch(dwMsgMapID) [/align]
[align=left] { [/align]
[align=left] case 0:[/align]
[align=left] if(uMsg == msg) [/align]
[align=left] { [/align]
[align=left] bHandled = TRUE; [/align]
[align=left] lResult = OnCreate(uMsg, wParam, lParam, bHandled); [/align]
[align=left] if(bHandled) [/align]
[align=left] return TRUE; [/align]
[align=left] }[/align]
[align=left] ……[/align]
[align=left] break; [/align]
[align=left] default: [/align]
[align=left] break; [/align]
[align=left] } [/align]
[align=left] return FALSE; [/align]
[align=left] }[/align]

5. Tips

对于一些技术点和平台作一定的介绍,以便于后续的学习。
5.1 UNREFERENCED_PARAMETER Macro
主要为消除M$ C++编译器在Level 4 (/W4)产生的C4100 :unreferenced formal parameter警告,但在/TC编译器选项下不可用。

消除C4100警告的还有另外一种常见的Unamed object的写法,如在 2.2 添加CWellcomeWindow.h中OnPaint(UINT /*uMsg*/...) ;这两种为消除C4100警告的写法,并无明显的优劣之分,且都是平台可移植的;但,从标准C++的编码规范来说,笔者更倾向于后者。
在M$ C++ Specification下还有其它的写法,但因其是M$ C++所特有,不便于学习标准C++,故在此不予考虑。
5.2 CXxx : public CYyy<CXxx>
此种声明在C++中是合法的,主要为实现编译时多态(Compile-time Polymophism)。

可参看如下代码:

[align=left]//CXxxx declarations[/align]
[align=left]#pragma once[/align]
[align=left]#include "stdafx.h"[/align]
[align=left] [/align]
[align=left]template <typename T>[/align]
[align=left]class CYyy {[/align]
[align=left]public:[/align]
[align=left] [/align]
[align=left] void Wellcome(const char* s) [/align]
[align=left] {[/align]
[align=left] T* pT = static_cast<T*>(this);[/align]
[align=left] pT->sayHello(s);[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] void sayHello(const char* s)[/align]
[align=left] {[/align]
[align=left] std::cout<<"Hi, "<<s<<std::endl;[/align]
[align=left] }[/align]
[align=left]};[/align]
[align=left] [/align]
[align=left]class CXxx1 : public CYyy<CXxx1> {[/align]
[align=left] [/align]
[align=left]};[/align]
[align=left] [/align]
[align=left]class CXxx2 : public CYyy<CXxx2> {[/align]
[align=left]public:[/align]
[align=left] void sayHello(const char* s) [/align]
[align=left] {[/align]
[align=left] std::cout<<"Wellcome, "<<s<<std::endl;[/align]
[align=left] }[/align]
[align=left]};[/align]
[align=left] [/align]
[align=left]// main entry point[/align]
[align=left]int _tmain(int argc, _TCHAR* argv[])[/align]
[align=left]{[/align]
[align=left] CXxx1 x1;[/align]
[align=left] CXxx2 x2;[/align]
[align=left] [/align]
[align=left] x1.Wellcome("Joe");[/align]
[align=left] x2.Wellcome("JoeM");[/align]
[align=left] [/align]
[align=left] return 0;[/align]
}

以上代码中Cyyy ::Wellcome相当于一个多态函数的一个包装器(wrapper),而所有的秘密就在T* pT = static_cast<T*>(this) 语句;编译时多态在空间开销上至少比运行时多态节省一个vtable指针的空间开销,且因其在编译时就已决定了函数入口点,故具有更高的执行效率。
5.3 throw()
C++ 异常规范(Exception Specifications),用于函数声明之后,通知编译器此函数不会抛出异常;但在ATL中主要目的是使M$ C 的SEH (Structured Exception Handling)在ATL中具有可移植性。

在ATL中经常可以看到这样的代码,如在atlwin.h中Cwindow的声明中:

[align=left]class CWindow[/align]
[align=left]{[/align]
[align=left]public:[/align]
[align=left] static RECT rcDefault;[/align]
[align=left] HWND m_hWnd;[/align]
[align=left] [/align]
[align=left] CWindow(HWND hWnd = NULL) throw() :[/align]
[align=left] m_hWnd(hWnd)[/align]
[align=left] {[/align]
[align=left] }[/align]
...
}

总结

Win32 vs. ATL
Windows Programming

目录

目录.. 2
概要.. 3
1. “Hello World!” in Win32. 3
1.1创建一个Win32 Project3
1.1.1 选择Win32 Project3
1.1.2 应用程序设置.. 4
1.1.3 编译器选项设置.. 5
1.2 Win32程序的基本结构.. 6
1.2.1 WinMain. 6
1.2.1 Register windows class. 7
1.2.3 Initialize application. 7
1.2.4 WndProc8
1.3 Build与程序输出.. 9
2. “Hello World!” in ATL. 9
2.1 修改stdafx.h. 9
2.2 添加CWellcomeWindow.h. 10
2.3 修改win32.cpp. 10
2.4 Build与程序输出.. 11
3. ATL vs. Win32. 12
3.1 编程模型.. 12
3.1.1 Win32基础构造块和主要流程.. 13
3.1.2 ATL基础构造块和主要流程.. 13
3.2 Build过程和程序输出.. 13
4. ATL Windows编程模型.. 14
4.1 RegisterClass在哪?. 14
4.1.1 DECLARE_WND_CLASS(NULL)宏定义.. 14
4.1.2 CWndClassInfo结构(_ATL_WNDCLASSINFOW)定义.. 14
4.1.3 ATL::CWindowImpl::Create定义.. 15
4.2 WndProc在哪?.. 15
4.2.1 Message-Map. 15
5. Tips. 16
5.1 UNREFERENCED_PARAMETER Macro. 16
5.2 CXxx : public CYyy<CXxx>. 17
5.3 throw()18
总结.. 18

概要

学习WTL可以有多种方式,当然如果有COM和ATL的知识背景最好不过,如果你有MFC编程背景却最为糟糕,除非你对MFC无所不知、无所不能: -)(如果你不是MFC的ORACLE,那么最好忘却它)

本系列打算从Win32和ATL入手,来学习WTL,情况理想的话的可以做到一举四得: Win32、ATL、WTL、C++(OO和泛型编程)

WTL的文档相对较少,且有些文档多是针对WTL3.0和WTL7.1,相对于最新的WTL8.0有些人老珠黄,也些范式已不太常用了,这也是促使写本系列的最初想法,帮助自己也帮助和我有同样需要的人,如果可以的话。

本系列中所用的工具和库为VS.NET Team Edition 2005,WTL80-6137;如果你没有VS2005,可以到M$站点http://msdn.microsoft.com/vstudio/express/ 上去下一个C++ Express;另外,假定你已安装好所有必备环境包括WTL了。

Part-1主要是从Win32和ATL着手,来讲解Windows编程的基本概念,有Win32和ATL基础的可以略过。: -)

建议:如果有条件的话最好跟随 1. “Hello World!” in Win322. “Hello World!” in ATL ,做完2个显示”Hello World!”的小程序,稍后将在 3. ATL vs. Win32 作进一步的讲解。

1. “Hello World!” in Win32

1.1创建一个Win32 Project

当然创建过程相当简单,主要目的是为了了解一下Win32程序的基本结构并为写第一个ATL程序作准备。

[align=left]在WndProc中:[/align]
[align=left] case WM_PAINT:[/align]
[align=left] hdc = BeginPaint(hWnd, &ps);[/align]
[align=left] // TODO: Add any drawing code here...[/align]
[align=left] EndPaint(hWnd, &ps);[/align]
[align=left] break;[/align]
[align=left]// TODO:标签下添加TextOut(hdc, 0, 0, _T("Hello world!"), 12);语句。[/align]

有3点需要注意的,请按图示:

1.1.1 选择Win32 Project
Figure-1.1-1



1.1.2 应用程序设置

Figure-1.1-2



采用默认设置。
1.1.3 编译器选项设置

Figure-1.1-3



[align=left]将C/C++ -> Advanced -> Compile As 设为Compile as C Code(/TC), 这点很容易被忽视,如果你要写纯血统M$ C而又不想过多的关注C/C++文件扩展名的话;如果,你设置了/TC编译器选项,请将 [/align]
[align=left]UNREFERENCED_PARAMETER(hPrevInstance);[/align]
[align=left] UNREFERENCED_PARAMETER(lpCmdLine);[/align]
[align=left]2句代码注释(commenting out)掉,关于UNREFERENCED_PARAMETER的使用将在5.1 UNREFERENCED_PARAMETER Macro 中谈到。[/align]

1.2 Win32程序的基本结构
VS2005向导产生的Win32 Project的块结构(block)和VS2003已有较大的改进了: -)。

以下代码均为了更清楚地说明主要问题而剔除了无关紧要的部分。
1.2.1 WinMain

[align=left]int APIENTRY _tWinMain(HINSTANCE hInstance,[/align]
[align=left] HINSTANCE hPrevInstance,[/align]
[align=left] LPTSTR lpCmdLine,[/align]
[align=left] int nCmdShow)[/align]
[align=left]{[/align]
[align=left] // TODO: Place code here.[/align]
[align=left] MSG msg;[/align]
[align=left] [/align]
[align=left] MyRegisterClass(hInstance);[/align]
[align=left] [/align]
[align=left] // Perform application initialization:[/align]
[align=left] if (!InitInstance (hInstance, nCmdShow))[/align]
[align=left] {[/align]
[align=left] return FALSE;[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] // Main message loop:[/align]
[align=left] while (GetMessage(&msg, NULL, 0, 0))[/align]
[align=left] {[/align]
[align=left] TranslateMessage(&msg);[/align]
[align=left] DispatchMessage(&msg);[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] return (int) msg.wParam;[/align]
[align=left]}[/align]

以上代码结构主要由3个块构成:Register windows class、Initialize application和Main message loop 。

1.2.1 Register windows class

[align=left]ATOM MyRegisterClass(HINSTANCE hInstance)[/align]
[align=left]{[/align]
[align=left] WNDCLASSEX wcex;[/align]
[align=left] [/align]
[align=left] wcex.cbSize = sizeof(WNDCLASSEX);[/align]
[align=left] [/align]
[align=left] wcex.style = CS_HREDRAW | CS_VREDRAW;[/align]
[align=left] wcex.lpfnWndProc = WndProc;[/align]
[align=left] wcex.cbClsExtra = 0;[/align]
[align=left] wcex.cbWndExtra = 0;[/align]
[align=left] wcex.hInstance = hInstance;[/align]
[align=left] wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32));[/align]
[align=left] wcex.hCursor = LoadCursor(NULL, IDC_ARROW);[/align]
[align=left] wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);[/align]
[align=left] wcex.lpszMenuName = MAKEINTRESOURCE(IDC_WIN32);[/align]
[align=left] wcex.lpszClassName = szWindowClass;[/align]
[align=left] wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));[/align]
[align=left] [/align]
[align=left] return RegisterClassEx(&wcex);[/align]
}

1.2.3 Initialize application
主要为CreateWindow、ShowWindow 。

[align=left]BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)[/align]
[align=left]{[/align]
[align=left] HWND hWnd;[/align]
[align=left] [/align]
[align=left] hInst = hInstance; // Store instance handle in our global variable[/align]
[align=left] [/align]
[align=left] hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,[/align]
[align=left] CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);[/align]
[align=left] [/align]
[align=left] if (!hWnd)[/align]
[align=left] {[/align]
[align=left] return FALSE;[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] ShowWindow(hWnd, nCmdShow);[/align]
[align=left] [/align]
[align=left] return TRUE;[/align]
[align=left]}[/align]

1.2.4 WndProc
在这里,主要为处理WM_PAINT和WM_DESTROY消息

[align=left]LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)[/align]
[align=left]{[/align]
[align=left] int wmId, wmEvent;[/align]
[align=left] PAINTSTRUCT ps;[/align]
[align=left] HDC hdc;[/align]
[align=left] [/align]
[align=left] switch (message)[/align]
[align=left] {[/align]
[align=left] case WM_PAINT:[/align]
[align=left] hdc = BeginPaint(hWnd, &ps);[/align]
[align=left] // TODO: Add any drawing code here...[/align]
[align=left] TextOut(hdc, 0, 0, _T("Hello world!"), 12);[/align]
[align=left] EndPaint(hWnd, &ps);[/align]
[align=left] break;[/align]
[align=left] case WM_DESTROY:[/align]
[align=left] PostQuitMessage(0);[/align]
[align=left] break;[/align]
[align=left] default:[/align]
[align=left] return DefWindowProc(hWnd, message, wParam, lParam);[/align]
[align=left] }[/align]
[align=left] return 0;[/align]
}

1.3 Build与程序输出



2. “Hello World!” in ATL

为了避免创建ATL COM EXE SERVER的开销和更好地说明Win32与ATL的内在关系,本例程序基于 1.1创建一个Win32 Project的程序创建。

对于ATL Windows编程陌生的,于此处不必过于在意,稍后在3. 对Win32和ATL的初步观察和比较会做一定的讲解。

2.1 修改stdafx.h
1.1创建的Win32代码中移除或注释掉stdafx.h中的:

[align=left]// Windows Header Files:[/align]
[align=left]#include <windows.h>[/align]
[align=left] [/align]
[align=left]// C RunTime Header Files[/align]
[align=left]#include <stdlib.h>[/align]
[align=left]#include <malloc.h>[/align]
[align=left]#include <memory.h>[/align]
[align=left]#include <tchar.h>[/align]

[align=left] [/align]
[align=left]并在stdafx.h中添加如下的include语句:[/align]
[align=left] [/align]

[align=left]#include <atlbase.h>[/align]
[align=left]extern CComModule _Module;[/align]
[align=left]#include <atlcom.h>[/align]
[align=left]#include <atlwin.h>[/align]
[align=left]#include "CWellcomeWindow.h"[/align]

[align=left] [/align]
2.2 添加CWellcomeWindow.h

[align=left]#pragma once[/align]
[align=left]#include "stdafx.h"[/align]
[align=left] [/align]
[align=left]class CWellcomeWindow : public CWindowImpl<CWellcomeWindow, [/align]
[align=left] CWindow, CFrameWinTraits> {[/align]
[align=left]public:[/align]
[align=left] DECLARE_WND_CLASS(NULL);[/align]
[align=left] [/align]
[align=left] BEGIN_MSG_MAP(CWellcomeWindow)[/align]
[align=left] MESSAGE_HANDLER(WM_CREATE, OnCreate)[/align]
[align=left] MESSAGE_HANDLER(WM_PAINT, OnPaint)[/align]
[align=left] MESSAGE_HANDLER(WM_DESTROY, OnDestroy)[/align]
[align=left] END_MSG_MAP()[/align]
[align=left] [/align]
[align=left] LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)[/align]
[align=left] {[/align]
[align=left] HICON appIcon = LoadIcon(_Module.GetResourceInstance(), [/align]
[align=left] MAKEINTRESOURCE(IDI_LEARNINGWTLPART1_ATL));[/align]
[align=left] this->SetIcon(appIcon);[/align]
[align=left] [/align]
[align=left] return 0;[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] [/align]
[align=left] LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)[/align]
[align=left] {[/align]
[align=left] PAINTSTRUCT ps;[/align]
[align=left] HDC hdc; // = this->GetDC();[/align]
[align=left] [/align]
[align=left] hdc = this->BeginPaint(&ps); // this->BeginPaint(&ps);[/align]
[align=left] TextOut(hdc, 0, 0, wellcome, wellcome.Length());[/align]
[align=left] this->EndPaint(&ps);[/align]
[align=left] //this->ReleaseDC(hdc);[/align]
[align=left] [/align]
[align=left] return 0;[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)[/align]
[align=left] {[/align]
[align=left] ::PostQuitMessage(0);[/align]
[align=left] [/align]
[align=left] return 0;[/align]
[align=left] }[/align]
};

2.3 修改win32.cpp
[align=left]保留WinMain方法声明和#include "stdafx.h"语句将其余代码删除或注释掉。添加_Module定义,修改WndMain方法体;完成后的代码如下:[/align]

[align=left]#include "stdafx.h"[/align]
[align=left]#include "LearningWTLPart1_ATL.h"[/align]
[align=left] [/align]
[align=left]CComModule _Module;[/align]
[align=left] [/align]
[align=left]int APIENTRY _tWinMain(HINSTANCE hInstance,[/align]
[align=left] HINSTANCE hPrevInstance,[/align]
[align=left] LPTSTR lpCmdLine,[/align]
[align=left] int nCmdShow)[/align]
[align=left]{[/align]
[align=left] UNREFERENCED_PARAMETER(hPrevInstance);[/align]
[align=left] UNREFERENCED_PARAMETER(lpCmdLine);[/align]
[align=left] [/align]
[align=left] _Module.Init(NULL, hInstance);[/align]
[align=left] [/align]
[align=left] CComBSTR appTitle;[/align]
[align=left] appTitle.LoadString(_Module.GetResourceInstance(), IDS_APP_TITLE);[/align]
[align=left] [/align]
[align=left] CWellcomeWindow wnd;[/align]
[align=left] wnd.Create(NULL, 0, appTitle);[/align]
[align=left] [/align]
[align=left] // Main message loop:[/align]
[align=left] MSG msg;[/align]
[align=left] while (GetMessage(&msg, NULL, 0, 0))[/align]
[align=left] {[/align]
[align=left] TranslateMessage(&msg);[/align]
[align=left] DispatchMessage(&msg);[/align]
[align=left] [/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] _Module.Term();[/align]
[align=left] [/align]
[align=left] return (int) msg.wParam;[/align]
[align=left]}[/align]

[align=left] [/align]
2.4 Build与程序输出
如果在1.1创建一个Win32 Project设置了编译器选项的,先将编译器选项设置恢复为默认值即/TP

程序输出如下:



以上的程序输出和 1.3 Build与程序输出对比,似乎缺点什么?!在Part-2再作交待。

3. ATL vs. Win32

首先,对Win32编程模型作了一个初步介绍,着重介绍ATL编程模型,这里并不存在对二者有孰优孰劣的假设;由于,对于作为M$ Windows程序员来说,或多或少、或直接或间接得都接触过Win32编程模型,故在本节中轻彼而重此。

其次,对Win32程序和ATL程序的Build过程和产生的可执行文件作一个初步的对照。
3.1 编程模型
Win32编程是整个Windows编程的基石,无论是ATL或WTL;因此,即使是ATL也必然包含Win32编程模型的基础构造块,只是ATL提供了轻量的基础构造块的封装(Encapsulation)。

Win32编程模型的基础构造块主要由Register windows class、Create window/Show window、Message loop和WndProc 4部分构成。

3.1.1 Win32基础构造块和主要流程



3.1.2 ATL基础构造块和主要流程



CComModule::Init & CComModule::Term : 是ATL关于COM Server的部分,在这里不必关注,在后续的WTL学习中还会相应地涉及到;

Main message loop : 在ATL中保留;

3.2 Build过程和程序输出
对Win32版与ATL版的Debug和Release分别作一个初步的对比;为说明一般性问题,在此不考虑编译器优化,因此,无论是Debug抑或Release均采用编译器默认设置。

[align=center][/align]
[align=center]Debug[/align]
[align=center]Release[/align]
[align=center]比重[/align]
Win32版
[align=right]100K[/align]
[align=right]68K[/align]
[align=center] [/align]
ATL版
[align=right]244K[/align]
[align=right]88K[/align]
[align=center]D244% | R129%[/align]
ATL版的Release,无论是物理文件大小还是运行时空间大小或运行效率都与Win32版的Release接近,且二者除系统库外,基本上不需要其它支持库(可能msvcrt.dll,gdi32.dll等)。

4. ATL Windows编程模型

参照3.1.1 Win32基础构造块和主要流程,来初步的了解ATL Windows编程模型(有关ATL Windows编程的文档极为“罕见“, 甚至与WTL相比 : -)) ;ATL Windows编程是WTL的基石,当然有必要在此费一番力气。
4.1 RegisterClass在哪?
1.2.1 Register windows class 中,MyRegisterClass函数要完成的工作:1. 初始化WNDCLASSEX;2. RegisterClassEx 。 而在ATL中,由CWellcomeWindow之由宏定义的成员函数(member function by macro defined)DECLARE_WND_CLASS(NULL)完成,其由wnd.Create(NULL, 0, appTitle) 调用(例如,在win32.cpp中)。

4.1.1 DECLARE_WND_CLASS(NULL)宏定义

[align=left]/////////////////////////////////////////////////////////////////////////////[/align]
[align=left]// CWndClassInfo - Manages Windows class information[/align]
[align=left] [/align]
[align=left]#define DECLARE_WND_CLASS(WndClassName) /[/align]
[align=left]static ATL::CWndClassInfo& GetWndClassInfo() /[/align]
[align=left]{ /[/align]
[align=left] static ATL::CWndClassInfo wc = /[/align]
[align=left] { /[/align]
[align=left] { sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS, StartWindowProc, /[/align]
[align=left] 0, 0, NULL, NULL, NULL, (HBRUSH)(COLOR_WINDOW + 1), NULL, WndClassName, NULL }, /[/align]
[align=left] NULL, NULL, IDC_ARROW, TRUE, 0, _T("") /[/align]
[align=left] }; /[/align]
[align=left] return wc; /[/align]
[align=left]}[/align]

CWndClassInfo为ATL内使用WNDCLASSEX结构的结构(C++),看看代码就应该有所了解了,关键在于 4.1.2 CWndClassInfo结构(_ATL_WNDCLASSINFOW)定义的黑体部分(Bold)。

4.1.2 CWndClassInfo结构(_ATL_WNDCLASSINFOW)定义

[align=left]struct _ATL_WNDCLASSINFOW[/align]
[align=left]{[/align]
[align=left] WNDCLASSEXW m_wc;[/align]
[align=left] LPCWSTR m_lpszOrigName;[/align]
[align=left] WNDPROC pWndProc;[/align]
[align=left] LPCWSTR m_lpszCursorID;[/align]
[align=left] BOOL m_bSystemCursor;[/align]
[align=left] ATOM m_atom;[/align]
[align=left] WCHAR m_szAutoName[5+sizeof(void*)*CHAR_BIT];[/align]
[align=left] ATOM Register(WNDPROC* p)[/align]
[align=left] {[/align]
[align=left] return AtlWinModuleRegisterWndClassInfoW(&_AtlWinModule, &_AtlBaseModule, this, p);[/align]
[align=left] }[/align]
[align=left]};[/align]

4.1.3 ATL::CWindowImpl::Create定义

[align=left]HWND Create(HWND hWndParent, _U_RECT rect = NULL, LPCTSTR szWindowName = NULL,[/align]
[align=left] DWORD dwStyle = 0, DWORD dwExStyle = 0,[/align]
[align=left] _U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)[/align]
[align=left] {[/align]
[align=left] if (T::GetWndClassInfo().m_lpszOrigName == NULL)[/align]
[align=left] T::GetWndClassInfo().m_lpszOrigName = GetWndClassName();[/align]
[align=left] ATOM atom = T::GetWndClassInfo().Register(&m_pfnSuperWindowProc);[/align]
[align=left] [/align]
[align=left] dwStyle = T::GetWndStyle(dwStyle);[/align]
[align=left] dwExStyle = T::GetWndExStyle(dwExStyle);[/align]
[align=left] [/align]
[align=left] // set caption[/align]
[align=left] if (szWindowName == NULL)[/align]
[align=left] szWindowName = T::GetWndCaption();[/align]
[align=left] [/align]
[align=left] return CWindowImplBaseT< TBase, TWinTraits >::Create(hWndParent, rect, szWindowName,[/align]
[align=left] dwStyle, dwExStyle, MenuOrID, atom, lpCreateParam);[/align]
[align=left] }[/align]

真正完成CreateWindow。
4.2 WndProc在哪?
Main message loop在ATL予以了保留(例如,在win32.cpp中)。

WndProc由宏定义成员函数完成,参看 2.2 添加CWellcomeWindow.h可以看到如下代码:

[align=left] BEGIN_MSG_MAP(CWellcomeWindow)[/align]
[align=left] MESSAGE_HANDLER(WM_CREATE, OnCreate)[/align]
[align=left] MESSAGE_HANDLER(WM_PAINT, OnPaint)[/align]
[align=left] MESSAGE_HANDLER(WM_DESTROY, OnDestroy)[/align]
[align=left] END_MSG_MAP()[/align]

4.2.1 Message-Map

[align=left]#define BEGIN_MSG_MAP(theClass) /[/align]
[align=left]public: /[/align]
[align=left] BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID = 0) /[/align]
[align=left] { /[/align]
[align=left] BOOL bHandled = TRUE; /[/align]
[align=left] (hWnd); /[/align]
[align=left] (uMsg); /[/align]
[align=left] (wParam); /[/align]
[align=left] (lParam); /[/align]
[align=left] (lResult); /[/align]
[align=left] (bHandled); /[/align]
[align=left] switch(dwMsgMapID) /[/align]
[align=left] { /[/align]
[align=left] case 0:[/align]
[align=left] [/align]
[align=left]#define END_MSG_MAP() /[/align]
[align=left] break; /[/align]
[align=left] default: /[/align]
[align=left] ATLTRACE(ATL::atlTraceWindowing, 0, _T("Invalid message map ID (%i)/n"), dwMsgMapID); /[/align]
[align=left] ATLASSERT(FALSE); /[/align]
[align=left] break; /[/align]
[align=left] } /[/align]
[align=left] return FALSE; /[/align]
[align=left] }[/align]

你所定义的特定的消息处理将会置于case 0defaul之间,例如 2.2 添加CWellcomeWindow.h将会产生如下代码:

[align=left]BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID = 0) [/align]
[align=left] { [/align]
[align=left] BOOL bHandled = TRUE; [/align]
[align=left] (hWnd); [/align]
[align=left] (uMsg); [/align]
[align=left] (wParam); [/align]
[align=left] (lParam); [/align]
[align=left] (lResult); [/align]
[align=left] (bHandled); [/align]
[align=left] switch(dwMsgMapID) [/align]
[align=left] { [/align]
[align=left] case 0:[/align]
[align=left] if(uMsg == msg) [/align]
[align=left] { [/align]
[align=left] bHandled = TRUE; [/align]
[align=left] lResult = OnCreate(uMsg, wParam, lParam, bHandled); [/align]
[align=left] if(bHandled) [/align]
[align=left] return TRUE; [/align]
[align=left] }[/align]
[align=left] ……[/align]
[align=left] break; [/align]
[align=left] default: [/align]
[align=left] break; [/align]
[align=left] } [/align]
[align=left] return FALSE; [/align]
[align=left] }[/align]

5. Tips

对于一些技术点和平台作一定的介绍,以便于后续的学习。
5.1 UNREFERENCED_PARAMETER Macro
主要为消除M$ C++编译器在Level 4 (/W4)产生的C4100 :unreferenced formal parameter警告,但在/TC编译器选项下不可用。

消除C4100警告的还有另外一种常见的Unamed object的写法,如在 2.2 添加CWellcomeWindow.h中OnPaint(UINT /*uMsg*/...) ;这两种为消除C4100警告的写法,并无明显的优劣之分,且都是平台可移植的;但,从标准C++的编码规范来说,笔者更倾向于后者。
在M$ C++ Specification下还有其它的写法,但因其是M$ C++所特有,不便于学习标准C++,故在此不予考虑。
5.2 CXxx : public CYyy<CXxx>
此种声明在C++中是合法的,主要为实现编译时多态(Compile-time Polymophism)。

可参看如下代码:

[align=left]//CXxxx declarations[/align]
[align=left]#pragma once[/align]
[align=left]#include "stdafx.h"[/align]
[align=left] [/align]
[align=left]template <typename T>[/align]
[align=left]class CYyy {[/align]
[align=left]public:[/align]
[align=left] [/align]
[align=left] void Wellcome(const char* s) [/align]
[align=left] {[/align]
[align=left] T* pT = static_cast<T*>(this);[/align]
[align=left] pT->sayHello(s);[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] void sayHello(const char* s)[/align]
[align=left] {[/align]
[align=left] std::cout<<"Hi, "<<s<<std::endl;[/align]
[align=left] }[/align]
[align=left]};[/align]
[align=left] [/align]
[align=left]class CXxx1 : public CYyy<CXxx1> {[/align]
[align=left] [/align]
[align=left]};[/align]
[align=left] [/align]
[align=left]class CXxx2 : public CYyy<CXxx2> {[/align]
[align=left]public:[/align]
[align=left] void sayHello(const char* s) [/align]
[align=left] {[/align]
[align=left] std::cout<<"Wellcome, "<<s<<std::endl;[/align]
[align=left] }[/align]
[align=left]};[/align]
[align=left] [/align]
[align=left]// main entry point[/align]
[align=left]int _tmain(int argc, _TCHAR* argv[])[/align]
[align=left]{[/align]
[align=left] CXxx1 x1;[/align]
[align=left] CXxx2 x2;[/align]
[align=left] [/align]
[align=left] x1.Wellcome("Joe");[/align]
[align=left] x2.Wellcome("JoeM");[/align]
[align=left] [/align]
[align=left] return 0;[/align]
}

以上代码中Cyyy ::Wellcome相当于一个多态函数的一个包装器(wrapper),而所有的秘密就在T* pT = static_cast<T*>(this) 语句;编译时多态在空间开销上至少比运行时多态节省一个vtable指针的空间开销,且因其在编译时就已决定了函数入口点,故具有更高的执行效率。
5.3 throw()
C++ 异常规范(Exception Specifications),用于函数声明之后,通知编译器此函数不会抛出异常;但在ATL中主要目的是使M$ C 的SEH (Structured Exception Handling)在ATL中具有可移植性。

在ATL中经常可以看到这样的代码,如在atlwin.h中Cwindow的声明中:

[align=left]class CWindow[/align]
[align=left]{[/align]
[align=left]public:[/align]
[align=left] static RECT rcDefault;[/align]
[align=left] HWND m_hWnd;[/align]
[align=left] [/align]
[align=left] CWindow(HWND hWnd = NULL) throw() :[/align]
[align=left] m_hWnd(hWnd)[/align]
[align=left] {[/align]
[align=left] }[/align]
...
}

总结
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: