您的位置:首页 > 编程语言

ATL窗口类源代码学习笔记

2004-11-12 13:22 405 查看

ATL 窗口类源代码学习笔记

本文是自己学习源代码的总结,在写作过程和察看代码寻找资料的时候,找到了些文章。

可参考:

WTL流程分析-初稿

ATL中的Thunk机制学习


1,CWindow



ATL有一个专门为窗口设计的基础类,可以做全部的窗口操作,这就是CWindow。它实际上就是对HWND操作的一个包装类,对几乎所有以HWND句柄为第一个参数的窗口API的进行了封装,例如:SetWindowText() 和 DestroyWindow()。CWindow类有一个公有成员m_hWnd,使你可以直接对窗口的句柄操作,CWindow还有一个操作符HWND,可以将CWindow对象传递给以HWND为参数的函数。

CWindow是一个普通的C++类。创建一个CWindow对象占用很少的资源,因为只有一个数据成员。


2,CWindowImpl继承树



在ATL类中对窗口过程的实现是CWindowImpl。CWindowImpl 含有所有窗口实现代码,例如:窗口类的注册,窗口的子类化,消息映射以及基本的WindowProc()函数。

我们先追根溯源,看看其继承过程:

一般来说,我们需要定义一个自己的窗口,首先就从CWindowImpl继承而来。

class CMyWnd : public CWindowImpl;

template

class ATL_NO_VTABLE CWindowImpl : public CWindowImplBaseT< TBase, TWinTraits >;

template

class ATL_NO_VTABLE CWindowImplBaseT : public CWindowImplRoot< TBase >;

template

class ATL_NO_VTABLE CWindowImplRoot : public TBase, public CMessageMap;

class ATL_NO_VTABLE CMessageMap

{

public:

virtual BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,

LRESULT& lResult, DWORD dwMsgMapID) = 0;

};


2.1,CMessageMap



该类是一个纯虚类,主要目的是提供对windows消息的处理接口。凡是继承自该类的子类,均有处理windows消息的函数了。


2.2,CWindowImplRoot



template

class ATL_NO_VTABLE CWindowImplRoot : public TBase, public CMessageMap;

首先看看TBase,这家伙是什么呢?就是我们在定义自己的窗口类的时候,传入的CWindow

class CMyWnd : public CWindowImpl;

TBase说明了我们自己定义的窗口类的祖先类是什么了。所以我们是不是可以从CWindow继承一个类来,然后将此子类作为类型参数传过去呢?完全可以。有了这个TBase,我们在写程序的时候,可以由自己控制从什么基类继承。

CWindowImplRoot定义了这几个成员,都是公有的。

CWndProcThunk m_thunk;

const _ATL_MSG* m_pCurrentMsg;

DWORD m_dwState;

它的几个成员函数主要是针对m_pCurrentMsg处理的。如:

// Current message

const _ATL_MSG* GetCurrentMessage() const

// "handled" management for cracked handlers

BOOL IsMsgHandled() const

void SetMsgHandled(BOOL bHandled)

还有就是对消息的推进和反射的支持方面。

// Message forwarding and reflection support

LRESULT ForwardNotifications(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);

LRESULT ReflectNotifications(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);

static BOOL DefaultReflectionHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult);

似乎对 m_dwState,m_thunk没有做什么处理。

关于CWndProcThunk,需要看:

ATL中的Thunk机制学习,
http://dev.csdn.net/develop/article/20/20532.shtm

2.3,CWindowImplBaseT



template

class ATL_NO_VTABLE CWindowImplBaseT : public CWindowImplRoot< TBase >;

看看CControlWinTraits这玩意。

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

// CWinTraits - Defines various default values for a window

template

class CWinTraits

{

public:

static DWORD GetWndStyle(DWORD dwStyle)

{

return dwStyle == 0 ? t_dwStyle : dwStyle;

}

static DWORD GetWndExStyle(DWORD dwExStyle)

{

return dwExStyle == 0 ? t_dwExStyle : dwExStyle;

}

};

typedef CWinTraits CControlWinTraits;

typedef CWinTraits CFrameWinTraits;

typedef CWinTraits CMDIChildWinTraits;

typedef CWinTraits<0, 0> CNullTraits;

template

class CWinTraitsOR

{

public:

static DWORD GetWndStyle(DWORD dwStyle)

{

return dwStyle | t_dwStyle | TWinTraits::GetWndStyle(dwStyle);

}

static DWORD GetWndExStyle(DWORD dwExStyle)

{

return dwExStyle | t_dwExStyle | TWinTraits::GetWndExStyle(dwExStyle);

}

};

原来它是对窗口属性的包装而已。定义都是静态函数。所以以后在创建窗口需要填入窗口属性参数的地方,我们可以简单的写:

CControlWinTraits::GetWndStyle(0);由此就得到了一个类似子控件的属性了。

或者我们可以定义自己的:

typedef CWinTraits CMyWindowTraits;

CWindowImplBaseT主要是对窗口过程的包装。

它的书记成员就一个窗口过程函数指针:

WNDPROC m_pfnSuperWindowProc;

该函数指针在构造的时候指向系统默认的窗口过程,和Win32 Api中一样的。

CWindowImplBaseT() : m_pfnSuperWindowProc(::DefWindowProc)

{}

而窗口属性在这个时候得到了支持了。

GetWndStyle

GetWndExStyle

有两个静态函数,也就是窗口过程函数吧,应该属于比较诡秘的技术,先略过。

WindowProc

StartWindowProc


2.4,CWindowImpl



template

class ATL_NO_VTABLE CWindowImpl : public CWindowImplBaseT< TBase, TWinTraits >

类中出现了一个宏:

DECLARE_WND_CLASS(NULL)

代表了什么呢?---〉

#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;

}

也就是定义了一个静态成员函数而已。

typedef struct tagWNDCLASSEXW {

UINT cbSize;

/* Win 3.x */

UINT style;

WNDPROC lpfnWndProc;

int cbClsExtra;

int cbWndExtra;

HINSTANCE hInstance;

HICON hIcon;

HCURSOR hCursor;

HBRUSH hbrBackground;

LPCWSTR lpszMenuName;

LPCWSTR lpszClassName;

/* Win 4.0 */

HICON hIconSm;

} WNDCLASSEXW, *PWNDCLASSEXW, NEAR *NPWNDCLASSEXW, FAR *LPWNDCLASSEXW;

窗口类的定义通过DECLARE_WND_CLASS宏或DECLARE_WND_CLASS_EX宏来实现。这辆个宏定义了一个 CWndClassInfo结构,这个结构封装了WNDCLASSEX结构。DECLARE_WND_CLASS宏让你指定窗口类的类名,其他参数使用默认设置,而DECLARE_WND_CLASS_EX宏还允许你指定窗口类的类型和窗口的背景颜色,你也可以用NULL作为类名,ATL会自动为你生成一个类名。

其实我们在定义自己的窗口类的时候,也定义了这么一个宏。

class CMyWnd : public CWindowImpl

{

...

public:

DECLARE_WND_CLASS(_T("My Window Class"))

...

};

除了这个宏外,就剩下一个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)


2.5,消息处理函数



接下来是我们在自定义类中对消息处理的定义,主要是使用类似下面的宏:

BEGIN_MSG_MAP(CMyWindow)

MESSAGE_HANDLER(WM_CLOSE, OnClose)

MESSAGE_HANDLER(WM_DESTROY, OnDestroy)

END_MSG_MAP()

至于这些宏还有哪些,以及其具体关系,我准备再写篇笔记学习之。上面的宏基本可以肯定的是,它定义了一个函数:

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

{

}

也就是消息处理函数,这个函数是个虚函数,定义在CMessageMap中,由CWindowImplRoot继承。

谁调用这个函数的呢?想想就该知道是在WindowProc中了,注册了窗口类后,成功创建完窗口,操作系统就会调用我们的窗口过程了。窗口过程应该在CWindowImplBaseT类中处理的。

template

LRESULT CALLBACK CWindowImplBaseT< TBase, TWinTraits >::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

CWindowImplBaseT< TBase, TWinTraits >* pThis = (CWindowImplBaseT< TBase, TWinTraits >*)hWnd;

// set a ptr to this message and save the old value

_ATL_MSG msg(pThis->m_hWnd, uMsg, wParam, lParam);

const _ATL_MSG* pOldMsg = pThis->m_pCurrentMsg;

pThis->m_pCurrentMsg = &msg;

// pass to the message map to process

LRESULT lRes;

BOOL bRet = pThis->ProcessWindowMessage(pThis->m_hWnd, uMsg, wParam, lParam, lRes, 0);

// restore saved value for the current message

ATLASSERT(pThis->m_pCurrentMsg == &msg);

pThis->m_pCurrentMsg = pOldMsg;

// do the default processing if message was not handled

if(!bRet)

{

if(uMsg != WM_NCDESTROY)

lRes = pThis->DefWindowProc(uMsg, wParam, lParam);

else

{

// unsubclass, if needed

LONG_PTR pfnWndProc = ::GetWindowLongPtr(pThis->m_hWnd, GWLP_WNDPROC);

lRes = pThis->DefWindowProc(uMsg, wParam, lParam);

if(pThis->m_pfnSuperWindowProc != ::DefWindowProc && ::GetWindowLongPtr(pThis->m_hWnd, GWLP_WNDPROC) == pfnWndProc)

::SetWindowLongPtr(pThis->m_hWnd, GWLP_WNDPROC, (LONG_PTR)pThis->m_pfnSuperWindowProc);

// mark window as destryed

pThis->m_dwState |= WINSTATE_DESTROYED;

}

}

if((pThis->m_dwState & WINSTATE_DESTROYED) && pThis->m_pCurrentMsg == NULL)

{

// clear out window handle

HWND hWnd = pThis->m_hWnd;

pThis->m_hWnd = NULL;

pThis->m_dwState &= ~WINSTATE_DESTROYED;

// clean up after window is destroyed

pThis->OnFinalMessage(hWnd);

}

return lRes;

}

CWindowImplBaseT< TBase, TWinTraits >* pThis = (CWindowImplBaseT< TBase, TWinTraits >*)hWnd;

这句有讲究,为何从一个句柄就可以得到类实例指针呢?我一时还没有搞明白。先不追究了。

在此函数中,主要点是:

(1)保存了原消息

(2)调用ProcessWindowMessage,也就是我们比较关注的消息处理函数调用源头。

(3)如果在自定义的窗口类中没有处理消息,则有ATL处理了。

(4)如果需要销毁窗口,则进行收尾处理,调用虚拟函数:OnFinalMessage。


总结:



通过上面对原代码的解读,我们可以发现ATL对窗口的创建,消息循环等的包装实质了。无非就是用模板机制,最终还是对Win32 API的调用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: