WTL 基础: WTL 创建一个窗口与Win32比较

           分析过CWindow 和CWindowImpl之后,已经了解了他们的构成。但是程序运行中创建一个窗口的具体过程仍需弄清楚。

           一个典型的win32程序,创建一个窗口的过程为:

          1.  注册窗口  

          2.  创建窗口

         3.   建立一个这个窗口的 WndProc

       注册窗口的目的是让操作系统知道它,然后就可以通过窗口的名称去建立窗口,操作系统就知道该窗口的相关信息。建立窗口的时候,还需要为这个窗口指定它自己的窗口函数 WndProc. 让窗口知道如何接收和处理自己的消息。

       注册窗口 

ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;
	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TESTWIN32));
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= MAKEINTRESOURCE(IDC_TESTWIN32);
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
	return RegisterClassEx(&wcex);
}
创建窗口

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;
   hInst = hInstance; // 将实例句柄存储在全局变量中
   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd)
      return FALSE;
   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);
   return TRUE;
}
窗口函数

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;

	switch (message)
	{
	case WM_COMMAND:
		wmId    = LOWORD(wParam);
		wmEvent = HIWORD(wParam);
		// 分析菜单选择:
		switch (wmId)
		{
		case IDM_ABOUT:
			DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
			break;
		case IDM_EXIT:
			DestroyWindow(hWnd);
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
		break;
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		// TODO: 在此添加任意绘图代码...
		EndPaint(hWnd, &ps);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

也就是说,要创建一个窗口,首先 要有一个WinProc函数,要配置 WNDCLASSEX,并用 RegisterClassEx(&wcex); 向windows注册,然后调用 CreateWindow 建立这个窗口。如果是windows 控件,那么因为是已知的已经向windows注册过得窗口,可以跳过前面的步骤,直接调用CreateWindow 创建窗口。
         DECLARE_WND_CLASS,DECLARE_WND_CLASS_EX和 DECLARE_WND_SUPERCLASS 三个红用于定义窗口属性,相当于完成WNDCLASSEX结构的填写。第二个宏添加了背景色和自己的样式,第二个用于建立子类(或叫做超类)。我们追逐这个宏,可以看到它们其实填写了这样的一个结构。

#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; \
}

请留意,WinProc位置,给出的窗口函数名为 StartWindowProc

struct _ATL_WNDCLASSINFOW
{
	WNDCLASSEXW m_wc;
	LPCWSTR m_lpszOrigName;
	WNDPROC pWndProc;
	LPCWSTR m_lpszCursorID;
	BOOL m_bSystemCursor;
	ATOM m_atom;
	WCHAR m_szAutoName[5+sizeof(void*)*CHAR_BIT];

	ATOM Register(_In_ WNDPROC* p)
	{
		return AtlWinModuleRegisterWndClassInfoW(&_AtlWinModule, &_AtlBaseModule, this, p);
	}
};
第一个成员就是 WNDCLASSEXW m_wc; 是  WNDCLASSEX 的UNICODE版本。

CWindowImpl 类里面的Create函数:

	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);
	}
};

这个函数先建立了注册信息,然后完成windows窗口注册, 最后调用基类的Create函数实际去建立窗口。

           需要注意的是 

      ATOM Register(_In_ WNDPROC* p)

        它是将 m_pfnSuperWindowProc 的指针带进去,如果该类有是从其他窗口派生的,那么这个成员会返回并保存基类的窗口函数。

         我们来看看另外一个宏: BEGIN_MSG_MAP, 它实际上定义了一个叫做 BOOL ProcessWindowMessage的函数。而这个函数得根在 CMessageMap 类里面,在哪里它被定义成纯虚函数。这个先说到这里,放下来。

          现在我们的目标是找窗口函数 winproc,注册的时候,它使用了  m_pfnSuperWindowProc, 而它是CWindwoImpl的基类 CWindowImplBaseT的成员变量,是一个窗口函数指针。初始化它的时候,是这样的。

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

::DefWindowProc 是一个windowAPI函数。微软的文档解释说,这是一个缺省的窗口处理函数。也就是说,如果不存在基类,那么它的基类窗口函数就是个缺省的窗口函数。

        现在,窗口已经建立好了,并且这个函数的窗口函数也已经注册:StartWindowProc,一旦有消息需要处理,就会调用这个窗口函数。

        正常情况下,这样处理就已经完了。窗口能够正确运行。但ATL 在这个地方来了个奇妙的变招。你会问了,我自己的窗口函数呢,我自己的消息什么时候处理呢? 这个变招解决了这个问题。

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);
}
看看这个地方,来了个变身,这个函数一但执行,窗口的winproc就被改变了。并且转向用另一个窗口函数 pThis->GetWindowProc()。这样一来,实际的窗口函数就变成了WindowProc,这也是一个静态函数。

pThis->m_thunk.Init(pThis->GetWindowProc(), pThis);
	WNDPROC pProc = pThis->m_thunk.GetWNDPROC();

这两句改变内容比较复杂, 其目的就是用This指针,代替了函数调用时候的hWnd参数。在下面的函数里面,在此将它转换回This指针。因为静态函数式没有this指针的。

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;
}


在这个函数里面 ProcessWindowMessage 函数被执行,消息处理执行了。


你可能感兴趣的:(Wtl)