您的位置:首页 > 其它

窗口子类化与超类化

2009-01-03 22:26 183 查看
模板类

CWindowImplBaseT

提供一个数据成员

WNDPROC m_pfnSuperWindowProc

并且初始化为

::DefWindowProc

。然而在窗口超类化处理时它存储了已注册窗口类的窗口过程,在窗口子类化时它保存窗口实例句柄原有的窗口过程,所有设置了
bHandled = false
的消息都由该数据成员处理。

template <class
TBase = CWindow, class TWinTraits = CControlWinTraits>
class ATL_NO_VTABLE
CWindowImplBaseT : public CWindowImplRoot< TBase >
{
public:
WNDPROC
m_pfnSuperWindowProc;

CWindowImplBaseT()
: m_pfnSuperWindowProc(::DefWindowProc)
{}
···
}

子类化
Subclass

子类化是在窗口实例创建之后,把窗口实例的窗口过程用另一个用户定义窗口类的窗口过程函数替换,从而改变其窗口行为。而窗口实例的原窗口过程则保存在数据成员

m_pfnSuperWindowProc

中。

template <class
TBase, class TWinTraits>
BOOL
CWindowImplBaseT< TBase, TWinTraits >::SubclassWindow(HWND hWnd)
{
BOOL
result;
ATLASSUME(m_hWnd
== NULL);
ATLASSERT(::IsWindow(hWnd));

//
Allocate the thunk structure here, where we can fail gracefully.

result
= m_thunk.Init(GetWindowProc(), this);
if
(result == FALSE)
{
return
FALSE;
}
WNDPROC
pProc = m_thunk.GetWNDPROC();
WNDPROC
pfnWndProc = (WNDPROC)::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)pProc);
if(pfnWndProc
== NULL)
return
FALSE;
m_pfnSuperWindowProc
= pfnWndProc;
m_hWnd
= hWnd;
return
TRUE;
}
窗口子类化函数接受一个有效窗口实例句柄
hWnd
,此窗口句柄必须是一个根据某个窗口类创建的窗口(如果是对话框模板资源上的控件窗口,则必然满足此要求)。

1
)把此窗口句柄
hWnd
对应的窗口类(超类窗口
Superclass
)的窗口过程设置为调用者窗口类(子类窗口
Subclass
)的窗口过程函数。

2

hWnd
原有窗口过程函数由数据成员
m_pfnSuperWindowProc
保存,便于把未处理的消息传递到原有窗口过程处理。

3
)调用者窗口类与窗口句柄
hWnd
挂接

m_hWnd = hWnd。

窗口子类化的行为在程序运行期有效。当窗口接收到

WM_NCDESTROY

消息即将被销毁时,如果该窗口存在窗口子类化的行为,则对此窗口类进行去子类化处理,即恢复
hWnd
对应的原窗口类的原窗口过程,并且窗口标识设置

WINSTATE_DESTROYED

位。具体实现如下:

template <class
TBase, class TWinTraits>
LRESULT CALLBACK
CWindowImplBaseT< TBase, TWinTraits >::WindowProc(HWND hWnd, UINT uMsg,
WPARAM wParam, LPARAM lParam)
{
···
//
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);

//
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;
}
}
···
return
lRes;
}
首先将消息传递到消息映射函数处理:
ProcessWindowMessage(pThis->m_hWnd,
uMsg, wParam, lParam, lRes, 0);
此函数是

CMessageLoop
接口类的唯一虚拟函数,完成具体的消息处理任务,在具体窗口类中由
BEGIN_MSG_MAP

wndclass

/END_MSG_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
ALT_MSG_MAP(msgMapID) /
break;
/
case
msgMapID:

#define
MESSAGE_HANDLER(msg, func) /
if(uMsg
== msg) /
{
/
bHandled
= TRUE; /
lResult
= func(uMsg, wParam, lParam, bHandled); /
if(bHandled)
/
return
TRUE; /
}

#define END_MSG_MAP()
/
break;
/
default:
/
ATLTRACE(ATL::atlTraceWindowing,
0, _T("Invalid message map ID (%i)/n"), dwMsgMapID); /
ATLASSERT(FALSE);
/
break;
/
} /
return
FALSE; /
}
可见所有的消息映射处理函数都在

ProcessWindowMessage

()中被按照相应的消息来调用,处理结果
LRESULT
由该函数的参数返回。此函数中还定义了一个极其重要的变量
bHandled
,它决定了此函数的返回值。该变量作为参数传递给消息处理函数,因此可以在消息映射函数中被设置。在窗口过程函数中若

ProcessWindowMessage()

返回
TRUE
则此消息处理到此结束,否则调用窗口类默认窗口函数:

LRESULT
DefWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
#ifdef STRICT
return
::CallWindowProc(m_pfnSuperWindowProc, m_hWnd, uMsg, wParam, lParam);
#else
return
::CallWindowProc((FARPROC)m_pfnSuperWindowProc, m_hWnd, uMsg, wParam, lParam);
#endif
}
仅仅是调用数据成员

m_pfnSuperWindowProc

保存的窗口过程函数进行处理。前面提到在窗口超类化处理时它存储了已注册窗口类的窗口过程,在窗口子类化时它保存窗口实例句柄原有的窗口过程,所有
bHandled = false
的消息都由该数据成员处理,也就是说超类化时路由到已注册窗口类窗口过程继续处理,子类化时路由到窗口实例句柄原有的窗口过程继续处理,否则,调用默认窗口过程函数处理。

因此在消息映射函数中可以通过设置
bHandled

来决定是否需要继续对此消息进行处理。然后窗口销毁时进行去子类化。当然如果需要在窗口销毁前去子类化,则可直接调用如下函数去子类化:

template <class
TBase, class TWinTraits>
HWND
CWindowImplBaseT< TBase, TWinTraits >::UnsubclassWindow(BOOL bForce /*=
FALSE*/)
{
ATLASSUME(m_hWnd
!= NULL);

WNDPROC
pOurProc = m_thunk.GetWNDPROC();
WNDPROC
pActiveProc = (WNDPROC)::GetWindowLongPtr(m_hWnd, GWLP_WNDPROC);

HWND
hWnd = NULL;
if
(bForce || pOurProc == pActiveProc)
{
if(!::SetWindowLongPtr(m_hWnd,
GWLP_WNDPROC, (LONG_PTR)m_pfnSuperWindowProc))
return
NULL;

m_pfnSuperWindowProc
= ::DefWindowProc;
hWnd
= m_hWnd;
m_hWnd
= NULL;
}
return
hWnd;
}
首先判断窗口实例句柄现有的窗口过程是否是当前窗口类的窗口过程,即是否存在子类化。若是并且强制要求去子类化,则恢复窗口实例句柄原有的窗口过程,并设置

m_pfnSuperWindowProc = ::DefWindowProc

存储默认窗口过程。最后窗口类与窗口实例句柄分离

m_hWnd = NULL。返回窗口实例句柄。

超类化

superclassing

超类化是根据已注册的窗口类来创建一个新的窗口类,这一过程是一个复制的过程,新建窗口类复制了已注册窗口类的所有内容,但是把窗口类名和窗口过程改为自己预定义的窗口类名和窗口过程。
然后注册新建窗口类,
同时设置数据成员

m_pfnSuperWindowProc

为已注册窗口类的窗口过程。

#define
DECLARE_WND_SUPERCLASS(WndClassName, OrigWndClassName) /
static
ATL::CWndClassInfo& GetWndClassInfo() /
{ /
static
ATL::CWndClassInfo wc = /
{
/
{
sizeof(WNDCLASSEX), 0, StartWindowProc, /

0, 0, NULL, NULL, NULL, NULL, NULL,
WndClassName, NULL }, /
OrigWndClassName,
NULL, NULL, TRUE, 0, _T("") /
};
/
return
wc; /
}
超类化宏设置了窗口类信息结构体的成员变量

OrigWndClassName

为一个已注册窗口类的窗口类名。

CWindowImpl
::
Create()函数中注册窗口类然后创建窗口实例。

ATOM atom =
T::GetWndClassInfo().Register(&m_pfnSuperWindowProc);
注册窗口类时传递了

m_pfnSuperWindowProc

作为参数,最终调用的是如下模板函数

template <class
T>
ATLINLINE ATOM
AtlModuleRegisterWndClassInfoT(_ATL_BASE_MODULE* pBaseModule, _ATL_WIN_MODULE*
pWinModule, typename T::_ATL_WNDCLASSINFO* p, WNDPROC* pProc, T)
{
···
if(p->m_atom
== 0)
{
if
(p->m_lpszOrigName != NULL)
{
ATLASSERT(pProc
!= NULL);
T::PCXSTR
lpsz = p->m_wc.lpszClassName;
WNDPROC
proc = p->m_wc.lpfnWndProc;

T::WNDCLASSEX
wc;
wc.cbSize
= sizeof(T::WNDCLASSEX);
//
Try global class
if(!T::GetClassInfoEx(NULL,
p->m_lpszOrigName, &wc))
{
//
try process local
if(!T::GetClassInfoEx(pBaseModule->m_hInst,
p->m_lpszOrigName, &wc))
{
ATLTRACE(atlTraceWindowing,
0, "ERROR : Could not obtain Window Class information for %s/n",
p->m_lpszOrigName);
return
0;
}
}
p->m_wc
= wc;
p->pWndProc
= p->m_wc.lpfnWndProc;
p->m_wc.lpszClassName
= lpsz;
p->m_wc.lpfnWndProc
= proc;
}

···
T::WNDCLASSEX
wcTemp;
wcTemp
= p->m_wc;
p->m_atom
= static_cast<ATOM>(T::GetClassInfoEx(p->m_wc.hInstance,
p->m_wc.lpszClassName, &wcTemp));
if
(p->m_atom == 0)
{
p->m_atom
= T::RegisterClassEx(pWinModule, &p->m_wc);
}
}

if
(p->m_lpszOrigName != NULL)
{
ATLASSERT(pProc
!= NULL);
ATLASSERT(p->pWndProc
!= NULL);
*pProc
= p->pWndProc;
}
return
p->m_atom;
}
函数判断如果有窗口超类化(p->m_lpszOrigName != NULL)则获取已注册窗口类的信息
wc
,然后

p->m_wc = wc;
p->pWndProc =
p->m_wc.lpfnWndProc;
p->m_wc.lpszClassName
= lpsz;
p->m_wc.lpfnWndProc
= proc;
以上语句的作用是:
令新建窗口类的

pWndProc

变量保存已注册窗口类窗口函数地址。

新建窗口类仅仅保留窗口类名和窗口过程函数不变,其余的都用子类窗口类相关信息替换。
注册新建窗口类,令新建窗口类

m_pfnSuperWindowProc

成员数据保存已注册窗口类的窗口过程函数地址。返回注册结果。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: