分享
 
 
 

ATL窗口类源代码学习笔记

王朝vc·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

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;

template

class ATL_NO_VTABLE CWindowImplBaseT : public CWindowImplRoot;

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;

看看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 CWinTraitsCControlWinTraits;

typedef CWinTraitsCFrameWinTraits;

typedef CWinTraitsCMDIChildWinTraits;

typedef CWinTraits 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

类中出现了一个宏:

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::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

CWindowImplBaseT* pThis = (CWindowImplBaseT*)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* pThis = (CWindowImplBaseT*)hWnd;

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

在此函数中,主要点是:

(1)保存了原消息

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

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

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

总结:

通过上面对原代码的解读,我们可以发现ATL对窗口的创建,消息循环等的包装实质了。无非就是用模板机制,最终还是对Win32 API的调用。

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