我们来看看CWindowImpl 是怎么回事,它与CWindow 是个什么样的关系。
template <class T, class TBase /* = CWindow */, class TWinTraits /* = CControlWinTraits */>
class ATL_NO_VTABLE CWindowImpl :
public CWindowImplBaseT< TBase, TWinTraits >
template <class TBase = CWindow, class TWinTraits = CControlWinTraits>
class ATL_NO_VTABLE CWindowImplBaseT :
public CWindowImplRoot< TBase >
template <class TBase /* = CWindow */>
class ATL_NO_VTABLE CWindowImplRoot :
public TBase,
public CMessageMap
这面代码显示了CWindowImpl诞生的逻辑。那我们就从 CWindowImplRoot 入手,来看看他们带来了什么。
CWindowImplRoot 类继承于两个基类,一个是CWindow(缺省), 一个是CMessageMap,也就是消息映射。暂时还不用仔细去捉摸消息映射具体情况。
public:
CWndProcThunk m_thunk;
const _ATL_MSG* m_pCurrentMsg;
DWORD m_dwState;
他有三个数据成员,一个是 CWndProcThunk 变量,一个是当前的消息指针,另个是一个状态变量。 接下来给出了几个函数,其中下面两个最重要。
LRESULT ForwardNotifications 向前通知,代码显示他特定的通知消息,直接发送给父窗口。
template <class TBase>
LRESULT CWindowImplRoot< TBase >::ForwardNotifications(
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam,
_Out_ BOOL& bHandled)
{
LRESULT lResult = 0;
switch(uMsg)
{
case WM_COMMAND:
case WM_NOTIFY:
case WM_PARENTNOTIFY:
case WM_DRAWITEM:
case WM_MEASUREITEM:
case WM_COMPAREITEM:
case WM_DELETEITEM:
case WM_VKEYTOITEM:
case WM_CHARTOITEM:
case WM_HSCROLL:
case WM_VSCROLL:
case WM_CTLCOLORBTN:
case WM_CTLCOLORDLG:
case WM_CTLCOLOREDIT:
case WM_CTLCOLORLISTBOX:
case WM_CTLCOLORMSGBOX:
case WM_CTLCOLORSCROLLBAR:
case WM_CTLCOLORSTATIC:
lResult = GetParent().SendMessage(uMsg, wParam, lParam);
break;
default:
bHandled = FALSE;
break;
}
return lResult;
}
而反射通知, 则是根据消息的种类,将消息变换成OCM消息(OCM__BASE + uMsg),发送给了相应的子窗口。
template <class TBase>
LRESULT CWindowImplRoot< TBase >::ReflectNotifications(
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam,
_Out_ BOOL& bHandled)
{
HWND hWndChild = NULL;
switch(uMsg)
{
case WM_COMMAND:
if(lParam != NULL) // not from a menu
hWndChild = (HWND)lParam;
break;
case WM_NOTIFY:
hWndChild = ((LPNMHDR)lParam)->hwndFrom;
break;
case WM_PARENTNOTIFY:
switch(LOWORD(wParam))
{
case WM_CREATE:
case WM_DESTROY:
hWndChild = (HWND)lParam;
break;
default:
hWndChild = GetDlgItem(HIWORD(wParam));
break;
}
break;
case WM_DRAWITEM:
if(wParam) // not from a menu
hWndChild = ((LPDRAWITEMSTRUCT)lParam)->hwndItem;
break;
case WM_MEASUREITEM:
if(wParam) // not from a menu
hWndChild = GetDlgItem(((LPMEASUREITEMSTRUCT)lParam)->CtlID);
break;
case WM_COMPAREITEM:
if(wParam) // not from a menu
hWndChild = ((LPCOMPAREITEMSTRUCT)lParam)->hwndItem;
break;
case WM_DELETEITEM:
if(wParam) // not from a menu
hWndChild = ((LPDELETEITEMSTRUCT)lParam)->hwndItem;
break;
case WM_VKEYTOITEM:
case WM_CHARTOITEM:
case WM_HSCROLL:
case WM_VSCROLL:
hWndChild = (HWND)lParam;
break;
case WM_CTLCOLORBTN:
case WM_CTLCOLORDLG:
case WM_CTLCOLOREDIT:
case WM_CTLCOLORLISTBOX:
case WM_CTLCOLORMSGBOX:
case WM_CTLCOLORSCROLLBAR:
case WM_CTLCOLORSTATIC:
hWndChild = (HWND)lParam;
break;
default:
break;
}
if(hWndChild == NULL)
{
bHandled = FALSE;
return 1;
}
ATLASSERT(::IsWindow(hWndChild));
return ::SendMessage(hWndChild, OCM__BASE + uMsg, wParam, lParam);
}
接着是: CWindowImplBaseT类, 它只有一个函数成员,就是窗口函数的指针 WNDPROC m_pfnSuperWindowProc,用来存放父类的窗口函数指针。
这个类也提供了几个函数,首先要捉摸一下 StartWindowProc
template <class TBase, class TWinTraits>
LRESULT CALLBACK CWindowImplBaseT< TBase, TWinTraits >::StartWindowProc(
_In_ HWND hWnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam)
{
CWindowImplBaseT< TBase, TWinTraits >* pThis = (CWindowImplBaseT< TBase, TWinTraits >*)_AtlWinModule.ExtractCreateWndData();
ATLASSERT(pThis != NULL);
if(!pThis)
{
return 0;
}
pThis->m_hWnd = hWnd;
// Initialize the thunk. This is allocated in CWindowImplBaseT::Create,
// so failure is unexpected here.
pThis->m_thunk.Init(pThis->GetWindowProc(), pThis);
WNDPROC pProc = pThis->m_thunk.GetWNDPROC();
WNDPROC pOldProc = (WNDPROC)::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)pProc);
#ifdef _DEBUG
// check if somebody has subclassed us already since we discard it
if(pOldProc != StartWindowProc)
ATLTRACE(atlTraceWindowing, 0, _T("Subclassing through a hook discarded.\n"));
#else
(pOldProc); // avoid unused warning
#endif
return pProc(hWnd, uMsg, wParam, lParam);
}
这个地方有点绕,不过总体的意思是为我们的窗口设置正确的窗口消息函数。如果想详细了解,可以参考下面的连接:
http://www.cnblogs.com/charm/archive/2011/04/19/2021031.html
WindowProc 函数,则是调用 CMessageMap 的消息处理函数,对消息进行执行处理了。
template <class TBase, class TWinTraits>
LRESULT CALLBACK CWindowImplBaseT< TBase, TWinTraits >::WindowProc(
_In_ HWND hWnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ 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 = 0;
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;
}
}
if((pThis->m_dwState & WINSTATE_DESTROYED) && pOldMsg== NULL)
{
// clear out window handle
HWND hWndThis = pThis->m_hWnd;
pThis->m_hWnd = NULL;
pThis->m_dwState &= ~WINSTATE_DESTROYED;
// clean up after window is destroyed
pThis->m_pCurrentMsg = pOldMsg;
pThis->OnFinalMessage(hWndThis);
}else {
pThis->m_pCurrentMsg = pOldMsg;
}
return lRes;
}
上面带代码相当复杂,中心意思就是启用正确的窗口消息函数,对窗口消息进行处理。差不多明白到这个程度应该就可以了。你当然可以去研究一下,如果不想被弄的疯掉的话,大致知道意思就可以停止了。毕竟,对于一个普通程序员来说,这已经足够了。
接下来的几个函数关系到窗口的创建,子类化窗口,和非子类化操作。
template <class TBase, class TWinTraits>
BOOL CWindowImplBaseT< TBase, TWinTraits >::SubclassWindow(_In_ HWND hWnd)
{
ATLASSUME(m_hWnd == NULL);
ATLASSERT(::IsWindow(hWnd));
// Allocate the thunk structure here, where we can fail gracefully.
BOOL 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;
}
子类化需要说明一下, 子类化的本意就是是的窗口的消息函数转换成子类的消息函数。
// Use only if you want to subclass before window is destroyed,
// WindowProc will automatically subclass when window goes away
template <class TBase, class TWinTraits>
HWND CWindowImplBaseT< TBase, TWinTraits >::UnsubclassWindow(_In_ 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;
}
非子类化,跟子类化过程相反。
然后来到了 CWindowImpl类,这里只提供了一个主要的函数,用于建立一个窗口。
HWND Create(
_In_opt_ HWND hWndParent,
_In_ _U_RECT rect = NULL,
_In_opt_z_ LPCTSTR szWindowName = NULL,
_In_ DWORD dwStyle = 0,
_In_ DWORD dwExStyle = 0,
_In_ _U_MENUorID MenuOrID = 0U,
_In_opt_ 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);
}
需要提一下这个虚函数, 每个窗口在销毁后都会自动调用这个函数。你可重载这个函数,并完成必要的清理工作。当然是你自己的清理工作。以前没注意过这个函数。所以这个地方特别提一下。
virtual void OnFinalMessage(_In_ HWND /*hWnd*/)