窗口化(windowing)实现形式有超类化(Superclass)和子类化(Subclass),下面一探究竟···
子类化 Subclass
子类化是在窗口实例创建之后,把其窗口过程设置为另一个用户定义窗口类的窗口过程函数,从而改变窗口行为。定义用户定义窗口即窗口子类化函数的调用者定义为子类窗口,而窗口实例所对应的窗口类为超类窗口。下面分析这一过程。
template
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
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 为超类窗口过程函数,否则为API ::DefWindowProc函数。
把消息传递给超类窗口过程和窗口系统API默认窗口过程处理。因此在消息映射函数中可以通过设置bHandled 来决定是否需要继续对此消息进行处理。然后窗口销毁时进行去子类化。
当然如果需要在窗口销毁前去子类化,则可直接调用如下函数去子类化:
template
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
超类化是根据已有的一个窗口类来创建一个新的窗口类,用已注册窗口类的窗口过程函数来作为新窗口类的窗口过程函数。定义新窗口类为超类窗口,已注册窗口类为子类窗口。
#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; /
}
超类化宏通过设置窗口类信息,改变超类窗口的窗口过程,即在注册窗口类时把超类窗口(WndClassName)的窗口过程设置为已注册子类窗口(OrigWndClassName)的窗口过程。