WTL 基础: CWindowImpl 发现

      我们来看看CWindowImpl 是怎么回事,它与CWindow 是个什么样的关系。

     

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

这面代码显示了CWindowImpl诞生的逻辑。那我们就从 CWindowImplRoot 入手,来看看他们带来了什么。

CWindowImplRoot 类继承于两个基类,一个是CWindow(缺省), 一个是CMessageMap,也就是消息映射。暂时还不用仔细去捉摸消息映射具体情况。

public:
	CWndProcThunk m_thunk;
	const _ATL_MSG* m_pCurrentMsg;
	DWORD m_dwState;

他有三个数据成员,一个是  CWndProcThunk 变量,一个是当前的消息指针,另个是一个状态变量。 接下来给出了几个函数,其中下面两个最重要。

LRESULT ForwardNotifications 向前通知,代码显示他特定的通知消息,直接发送给父窗口。

template 
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 
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 
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 
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 
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 
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*/)


你可能感兴趣的:(Wtl)