分享
 
 
 

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

王朝system·作者佚名  2007-06-16
窄屏简体版  字體: |||超大  

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程序作准备。

在WndProc中:

case WM_PAINT:

hdc = BeginPaint(hWnd, &ps);

// TODO: Add any drawing code here...

EndPaint(hWnd, &ps);

break;

// TODO:标签下添加TextOut(hdc, 0, 0, _T("Hello world!"), 12);语句。

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

1.1.1 选择Win32 Project

Figure-1.1-1

1.1.2 应用程序设置

Figure-1.1-2

采用默认设置。

1.1.3 编译器选项设置

Figure-1.1-3

将C/C++ -> Advanced -> Compile As 设为Compile as C Code(/TC), 这点很容易被忽视,如果你要写纯血统M$ C而又不想过多的关注C/C++文件扩展名的话;如果,你设置了/TC编译器选项,请将

UNREFERENCED_PARAMETER(hPrevInstance);

UNREFERENCED_PARAMETER(lpCmdLine);

2句代码注释(commenting out)掉,关于UNREFERENCED_PARAMETER的使用将在5.1 UNREFERENCED_PARAMETER Macro 中谈到。

1.2 Win32程序的基本结构

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

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

1.2.1 WinMain

int APIENTRY _tWinMain(HINSTANCE hInstance,

HINSTANCE hPrevInstance,

LPTSTR lpCmdLine,

int nCmdShow)

{

// TODO: Place code here.

MSG msg;

MyRegisterClass(hInstance);

// Perform application initialization:

if (!InitInstance (hInstance, nCmdShow))

{

return FALSE;

}

// Main message loop:

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

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

return (int) msg.wParam;

}

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

1.2.1 Register windows class

ATOM MyRegisterClass(HINSTANCE hInstance)

{

WNDCLASSEX wcex;

wcex.cbSize = sizeof(WNDCLASSEX);

wcex.style = CS_HREDRAW | CS_VREDRAW;

wcex.lpfnWndProc = WndProc;

wcex.cbClsExtra = 0;

wcex.cbWndExtra = 0;

wcex.hInstance = hInstance;

wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32));

wcex.hCursor = LoadCursor(NULL, IDC_ARROW);

wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);

wcex.lpszMenuName = MAKEINTRESOURCE(IDC_WIN32);

wcex.lpszClassName = szWindowClass;

wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

return RegisterClassEx(&wcex);

}

1.2.3 Initialize application

主要为CreateWindow、ShowWindow 。

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)

{

HWND hWnd;

hInst = hInstance; // Store instance handle in our global variable

hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,

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

if (!hWnd)

{

return FALSE;

}

ShowWindow(hWnd, nCmdShow);

return TRUE;

}

1.2.4 WndProc

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

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

{

int wmId, wmEvent;

PAINTSTRUCT ps;

HDC hdc;

switch (message)

{

case WM_PAINT:

hdc = BeginPaint(hWnd, &ps);

// TODO: Add any drawing code here...

TextOut(hdc, 0, 0, _T("Hello world!"), 12);

EndPaint(hWnd, &ps);

break;

case WM_DESTROY:

PostQuitMessage(0);

break;

default:

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

}

return 0;

}

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中的:

// Windows Header Files:

#include <windows.h>

// C RunTime Header Files

#include <stdlib.h>

#include <malloc.h>

#include <memory.h>

#include <tchar.h>

并在stdafx.h中添加如下的include语句:

#include <atlbase.h>

extern CComModule _Module;

#include <atlcom.h>

#include <atlwin.h>

#include "CWellcomeWindow.h"

2.2 添加CWellcomeWindow.h

#pragma once

#include "stdafx.h"

class CWellcomeWindow : public CWindowImpl<CWellcomeWindow,

CWindow, CFrameWinTraits> {

public:

DECLARE_WND_CLASS(NULL);

BEGIN_MSG_MAP(CWellcomeWindow)

MESSAGE_HANDLER(WM_CREATE, OnCreate)

MESSAGE_HANDLER(WM_PAINT, OnPaint)

MESSAGE_HANDLER(WM_DESTROY, OnDestroy)

END_MSG_MAP()

LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)

{

HICON appIcon = LoadIcon(_Module.GetResourceInstance(),

MAKEINTRESOURCE(IDI_LEARNINGWTLPART1_ATL));

this->SetIcon(appIcon);

return 0;

}

LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)

{

PAINTSTRUCT ps;

CComBSTR wellcome(_T("Hello World!"));

HDC hdc; // = this->GetDC();

hdc = this->BeginPaint(&ps); // this->BeginPaint(&ps);

TextOut(hdc, 0, 0, wellcome, wellcome.Length());

this->EndPaint(&ps);

//this->ReleaseDC(hdc);

return 0;

}

LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)

{

::PostQuitMessage(0);

return 0;

}

};

2.3 修改win32.cpp

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

#include "stdafx.h"

#include "LearningWTLPart1_ATL.h"

CComModule _Module;

int APIENTRY _tWinMain(HINSTANCE hInstance,

HINSTANCE hPrevInstance,

LPTSTR lpCmdLine,

int nCmdShow)

{

UNREFERENCED_PARAMETER(hPrevInstance);

UNREFERENCED_PARAMETER(lpCmdLine);

_Module.Init(NULL, hInstance);

CComBSTR appTitle;

appTitle.LoadString(_Module.GetResourceInstance(), IDS_APP_TITLE);

CWellcomeWindow wnd;

wnd.Create(NULL, 0, appTitle);

// Main message loop:

MSG msg;

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

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

_Module.Term();

return (int) msg.wParam;

}

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均采用编译器默认设置。

Debug

Release

比重

Win32版

100K

68K

ATL版

244K

88K

D244% | R129%

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)宏定义

/////////////////////////////////////////////////////////////////////////////

// CWndClassInfo - Manages Windows class information

#define DECLARE_WND_CLASS(WndClassName) static ATL::CWndClassInfo& GetWndClassInfo() { static ATL::CWndClassInfo wc = { { sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS, StartWindowProc, 0, 0, NULL, NULL, NULL, (HBRUSH)(COLOR_WINDOW + 1), NULL, WndClassName, NULL }, NULL, NULL, IDC_ARROW, TRUE, 0, _T("") }; return wc; }

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

4.1.2 CWndClassInfo结构(_ATL_WNDCLASSINFOW)定义

struct _ATL_WNDCLASSINFOW

{

WNDCLASSEXW m_wc;

LPCWSTR m_lpszOrigName;

WNDPROC pWndProc;

LPCWSTR m_lpszCursorID;

BOOL m_bSystemCursor;

ATOM m_atom;

WCHAR m_szAutoName[5+sizeof(void*)*CHAR_BIT];

ATOM Register(WNDPROC* p)

{

return AtlWinModuleRegisterWndClassInfoW(&_AtlWinModule, &_AtlBaseModule, this, p);

}

};

4.1.3 ATL::CWindowImpl::Create定义

HWND Create(HWND hWndParent, _U_RECT rect = NULL, LPCTSTR szWindowName = NULL,

DWORD dwStyle = 0, DWORD dwExStyle = 0,

_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)

{

if (T::GetWndClassInfo().m_lpszOrigName == NULL)

T::GetWndClassInfo().m_lpszOrigName = GetWndClassName();

ATOM atom = T::GetWndClassInfo().Register(&m_pfnSuperWindowProc);

dwStyle = T::GetWndStyle(dwStyle);

dwExStyle = T::GetWndExStyle(dwExStyle);

// set caption

if (szWindowName == NULL)

szWindowName = T::GetWndCaption();

return CWindowImplBaseT< TBase, TWinTraits >::Create(hWndParent, rect, szWindowName,

dwStyle, dwExStyle, MenuOrID, atom, lpCreateParam);

}

真正完成CreateWindow。

4.2 WndProc在哪?

Main message loop在ATL予以了保留(例如,在win32.cpp中)。

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

BEGIN_MSG_MAP(CWellcomeWindow)

MESSAGE_HANDLER(WM_CREATE, OnCreate)

MESSAGE_HANDLER(WM_PAINT, OnPaint)

MESSAGE_HANDLER(WM_DESTROY, OnDestroy)

END_MSG_MAP()

4.2.1 Message-Map

#define BEGIN_MSG_MAP(theClass) public: BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID = 0) { BOOL bHandled = TRUE; (hWnd); (uMsg); (wParam); (lParam); (lResult); (bHandled); switch(dwMsgMapID) { case 0:

#define END_MSG_MAP()

break; default: ATLTRACE(ATL::atlTraceWindowing, 0, _T("Invalid message map ID (%i)\n"), dwMsgMapID); ATLASSERT(FALSE);

break; } return FALSE; }

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

BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID = 0)

{

BOOL bHandled = TRUE;

(hWnd);

(uMsg);

(wParam);

(lParam);

(lResult);

(bHandled);

switch(dwMsgMapID)

{

case 0:

if(uMsg == msg)

{

bHandled = TRUE;

lResult = OnCreate(uMsg, wParam, lParam, bHandled);

if(bHandled)

return TRUE;

}

……

break;

default:

break;

}

return FALSE;

}

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)。

可参看如下代码:

//CXxxx declarations

#pragma once

#include "stdafx.h"

template <typename T>

class CYyy {

public:

void Wellcome(const char* s)

{

T* pT = static_cast<T*>(this);

pT->sayHello(s);

}

void sayHello(const char* s)

{

std::cout<<"Hi, "<<s<<std::endl;

}

};

class CXxx1 : public CYyy<CXxx1> {

};

class CXxx2 : public CYyy<CXxx2> {

public:

void sayHello(const char* s)

{

std::cout<<"Wellcome, "<<s<<std::endl;

}

};

// main entry point

int _tmain(int argc, _TCHAR* argv[])

{

CXxx1 x1;

CXxx2 x2;

x1.Wellcome("Joe");

x2.Wellcome("JoeM");

return 0;

}

以上代码中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的声明中:

class CWindow

{

public:

static RECT rcDefault;

HWND m_hWnd;

CWindow(HWND hWnd = NULL) throw() :

m_hWnd(hWnd)

{

}

...

}

总结

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程序作准备。

在WndProc中:

case WM_PAINT:

hdc = BeginPaint(hWnd, &ps);

// TODO: Add any drawing code here...

EndPaint(hWnd, &ps);

break;

// TODO:标签下添加TextOut(hdc, 0, 0, _T("Hello world!"), 12);语句。

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

1.1.1 选择Win32 Project

Figure-1.1-1

1.1.2 应用程序设置

Figure-1.1-2

采用默认设置。

1.1.3 编译器选项设置

Figure-1.1-3

将C/C++ -> Advanced -> Compile As 设为Compile as C Code(/TC), 这点很容易被忽视,如果你要写纯血统M$ C而又不想过多的关注C/C++文件扩展名的话;如果,你设置了/TC编译器选项,请将

UNREFERENCED_PARAMETER(hPrevInstance);

UNREFERENCED_PARAMETER(lpCmdLine);

2句代码注释(commenting out)掉,关于UNREFERENCED_PARAMETER的使用将在5.1 UNREFERENCED_PARAMETER Macro 中谈到。

1.2 Win32程序的基本结构

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

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

1.2.1 WinMain

int APIENTRY _tWinMain(HINSTANCE hInstance,

HINSTANCE hPrevInstance,

LPTSTR lpCmdLine,

int nCmdShow)

{

// TODO: Place code here.

MSG msg;

MyRegisterClass(hInstance);

// Perform application initialization:

if (!InitInstance (hInstance, nCmdShow))

{

return FALSE;

}

// Main message loop:

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

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

return (int) msg.wParam;

}

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

1.2.1 Register windows class

ATOM MyRegisterClass(HINSTANCE hInstance)

{

WNDCLASSEX wcex;

wcex.cbSize = sizeof(WNDCLASSEX);

wcex.style = CS_HREDRAW | CS_VREDRAW;

wcex.lpfnWndProc = WndProc;

wcex.cbClsExtra = 0;

wcex.cbWndExtra = 0;

wcex.hInstance = hInstance;

wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32));

wcex.hCursor = LoadCursor(NULL, IDC_ARROW);

wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);

wcex.lpszMenuName = MAKEINTRESOURCE(IDC_WIN32);

wcex.lpszClassName = szWindowClass;

wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

return RegisterClassEx(&wcex);

}

1.2.3 Initialize application

主要为CreateWindow、ShowWindow 。

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)

{

HWND hWnd;

hInst = hInstance; // Store instance handle in our global variable

hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,

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

if (!hWnd)

{

return FALSE;

}

ShowWindow(hWnd, nCmdShow);

return TRUE;

}

1.2.4 WndProc

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

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

{

int wmId, wmEvent;

PAINTSTRUCT ps;

HDC hdc;

switch (message)

{

case WM_PAINT:

hdc = BeginPaint(hWnd, &ps);

// TODO: Add any drawing code here...

TextOut(hdc, 0, 0, _T("Hello world!"), 12);

EndPaint(hWnd, &ps);

break;

case WM_DESTROY:

PostQuitMessage(0);

break;

default:

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

}

return 0;

}

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中的:

// Windows Header Files:

#include <windows.h>

// C RunTime Header Files

#include <stdlib.h>

#include <malloc.h>

#include <memory.h>

#include <tchar.h>

并在stdafx.h中添加如下的include语句:

#include <atlbase.h>

extern CComModule _Module;

#include <atlcom.h>

#include <atlwin.h>

#include "CWellcomeWindow.h"

2.2 添加CWellcomeWindow.h

#pragma once

#include "stdafx.h"

class CWellcomeWindow : public CWindowImpl<CWellcomeWindow,

CWindow, CFrameWinTraits> {

public:

DECLARE_WND_CLASS(NULL);

BEGIN_MSG_MAP(CWellcomeWindow)

MESSAGE_HANDLER(WM_CREATE, OnCreate)

MESSAGE_HANDLER(WM_PAINT, OnPaint)

MESSAGE_HANDLER(WM_DESTROY, OnDestroy)

END_MSG_MAP()

LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)

{

HICON appIcon = LoadIcon(_Module.GetResourceInstance(),

MAKEINTRESOURCE(IDI_LEARNINGWTLPART1_ATL));

this->SetIcon(appIcon);

return 0;

}

LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)

{

PAINTSTRUCT ps;

HDC hdc; // = this->GetDC();

hdc = this->BeginPaint(&ps); // this->BeginPaint(&ps);

TextOut(hdc, 0, 0, wellcome, wellcome.Length());

this->EndPaint(&ps);

//this->ReleaseDC(hdc);

return 0;

}

LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)

{

::PostQuitMessage(0);

return 0;

}

};

2.3 修改win32.cpp

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

#include "stdafx.h"

#include "LearningWTLPart1_ATL.h"

CComModule _Module;

int APIENTRY _tWinMain(HINSTANCE hInstance,

HINSTANCE hPrevInstance,

LPTSTR lpCmdLine,

int nCmdShow)

{

UNREFERENCED_PARAMETER(hPrevInstance);

UNREFERENCED_PARAMETER(lpCmdLine);

_Module.Init(NULL, hInstance);

CComBSTR appTitle;

appTitle.LoadString(_Module.GetResourceInstance(), IDS_APP_TITLE);

CWellcomeWindow wnd;

wnd.Create(NULL, 0, appTitle);

// Main message loop:

MSG msg;

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

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

_Module.Term();

return (int) msg.wParam;

}

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均采用编译器默认设置。

Debug

Release

比重

Win32版

100K

68K

ATL版

244K

88K

D244% | R129%

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)宏定义

/////////////////////////////////////////////////////////////////////////////

// CWndClassInfo - Manages Windows class information

#define DECLARE_WND_CLASS(WndClassName) static ATL::CWndClassInfo& GetWndClassInfo() { static ATL::CWndClassInfo wc = { { sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS, StartWindowProc, 0, 0, NULL, NULL, NULL, (HBRUSH)(COLOR_WINDOW + 1), NULL, WndClassName, NULL }, NULL, NULL, IDC_ARROW, TRUE, 0, _T("") }; return wc; }

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

4.1.2 CWndClassInfo结构(_ATL_WNDCLASSINFOW)定义

struct _ATL_WNDCLASSINFOW

{

WNDCLASSEXW m_wc;

LPCWSTR m_lpszOrigName;

WNDPROC pWndProc;

LPCWSTR m_lpszCursorID;

BOOL m_bSystemCursor;

ATOM m_atom;

WCHAR m_szAutoName[5+sizeof(void*)*CHAR_BIT];

ATOM Register(WNDPROC* p)

{

return AtlWinModuleRegisterWndClassInfoW(&_AtlWinModule, &_AtlBaseModule, this, p);

}

};

4.1.3 ATL::CWindowImpl::Create定义

HWND Create(HWND hWndParent, _U_RECT rect = NULL, LPCTSTR szWindowName = NULL,

DWORD dwStyle = 0, DWORD dwExStyle = 0,

_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)

{

if (T::GetWndClassInfo().m_lpszOrigName == NULL)

T::GetWndClassInfo().m_lpszOrigName = GetWndClassName();

ATOM atom = T::GetWndClassInfo().Register(&m_pfnSuperWindowProc);

dwStyle = T::GetWndStyle(dwStyle);

dwExStyle = T::GetWndExStyle(dwExStyle);

// set caption

if (szWindowName == NULL)

szWindowName = T::GetWndCaption();

return CWindowImplBaseT< TBase, TWinTraits >::Create(hWndParent, rect, szWindowName,

dwStyle, dwExStyle, MenuOrID, atom, lpCreateParam);

}

真正完成CreateWindow。

4.2 WndProc在哪?

Main message loop在ATL予以了保留(例如,在win32.cpp中)。

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

BEGIN_MSG_MAP(CWellcomeWindow)

MESSAGE_HANDLER(WM_CREATE, OnCreate)

MESSAGE_HANDLER(WM_PAINT, OnPaint)

MESSAGE_HANDLER(WM_DESTROY, OnDestroy)

END_MSG_MAP()

4.2.1 Message-Map

#define BEGIN_MSG_MAP(theClass) public: BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID = 0) { BOOL bHandled = TRUE; (hWnd); (uMsg); (wParam); (lParam); (lResult); (bHandled); switch(dwMsgMapID) { case 0:

#define END_MSG_MAP()

break; default: ATLTRACE(ATL::atlTraceWindowing, 0, _T("Invalid message map ID (%i)\n"), dwMsgMapID); ATLASSERT(FALSE);

break; } return FALSE; }

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

BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID = 0)

{

BOOL bHandled = TRUE;

(hWnd);

(uMsg);

(wParam);

(lParam);

(lResult);

(bHandled);

switch(dwMsgMapID)

{

case 0:

if(uMsg == msg)

{

bHandled = TRUE;

lResult = OnCreate(uMsg, wParam, lParam, bHandled);

if(bHandled)

return TRUE;

}

……

break;

default:

break;

}

return FALSE;

}

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)。

可参看如下代码:

//CXxxx declarations

#pragma once

#include "stdafx.h"

template <typename T>

class CYyy {

public:

void Wellcome(const char* s)

{

T* pT = static_cast<T*>(this);

pT->sayHello(s);

}

void sayHello(const char* s)

{

std::cout<<"Hi, "<<s<<std::endl;

}

};

class CXxx1 : public CYyy<CXxx1> {

};

class CXxx2 : public CYyy<CXxx2> {

public:

void sayHello(const char* s)

{

std::cout<<"Wellcome, "<<s<<std::endl;

}

};

// main entry point

int _tmain(int argc, _TCHAR* argv[])

{

CXxx1 x1;

CXxx2 x2;

x1.Wellcome("Joe");

x2.Wellcome("JoeM");

return 0;

}

以上代码中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的声明中:

class CWindow

{

public:

static RECT rcDefault;

HWND m_hWnd;

CWindow(HWND hWnd = NULL) throw() :

m_hWnd(hWnd)

{

}

...

}

总结

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
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- 王朝網路 版權所有