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

来源:互联网 发布:股票统计软件 编辑:程序博客网 时间:2024/06/18 10:41

           分析过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 captionif (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 <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 itif(pOldProc != StartWindowProc)ATLTRACE(atlTraceWindowing, 0, _T("Subclassing through a hook discarded.\n"));#else(pOldProc);// avoid unused warning#endifreturn 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 <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 processLRESULT lRes = 0;BOOL bRet = pThis->ProcessWindowMessage(pThis->m_hWnd, uMsg, wParam, lParam, lRes, 0);// restore saved value for the current messageATLASSERT(pThis->m_pCurrentMsg == &msg);// do the default processing if message was not handledif(!bRet){if(uMsg != WM_NCDESTROY)lRes = pThis->DefWindowProc(uMsg, wParam, lParam);else{// unsubclass, if neededLONG_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 destryedpThis->m_dwState |= WINSTATE_DESTROYED;}}if((pThis->m_dwState & WINSTATE_DESTROYED) && pOldMsg== NULL){// clear out window handleHWND hWndThis = pThis->m_hWnd;pThis->m_hWnd = NULL;pThis->m_dwState &= ~WINSTATE_DESTROYED;// clean up after window is destroyedpThis->m_pCurrentMsg = pOldMsg;pThis->OnFinalMessage(hWndThis);}else {pThis->m_pCurrentMsg = pOldMsg;}return lRes;}


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


0 0
原创粉丝点击