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 函数被执行,消息处理执行了。
- WTL 基础: WTL 创建一个窗口与Win32比较
- WTL 窗口创建消息队列
- WTL
- WTL
- WTL
- WTL
- WTL
- WTL
- WTL
- WTL
- wTL
- WTL
- WTL
- WTL
- WTL
- WTL的窗口子类化与超类化
- WTL,ATL与MFC之比较
- WTL,ATL与MFC之比较
- java自定义类型参数的方法时出现NoSuchMethodException
- 黑马程序员 Java多线程
- 和四个方向的尖角
- Linux学习记录--文件管理相关系统编程
- UIKit 01、使用Xcode4.5、XCode 5.1 创建 iOS应用程序
- WTL 基础: WTL 创建一个窗口与Win32比较
- 设计模式之创建新模式
- 动态SQL、绑定变量、静态SQL的性能对比
- navicat执行sql文件时,中文乱码的处理
- 在Html<span>添加CSS样式
- 个人知识管理
- Velocity宏定义的坑与解决办法
- ORALCE 的字符串转换为列表
- jQuery Plugin Boilerplate