WTL的核心机制
WTL背景介紹
WTL是微軟ATL開發(fā)組成員Nenad Stefanovic先生在ATL Windowing機(jī)制上發(fā)展起來的一整套GUI框架,運(yùn)用template技術(shù)組織和創(chuàng)建GUI對象,構(gòu)筑了精致的面向?qū)ο罂蚣?#xff08;在這里object oriented與template達(dá)成了精致的融合)。雖然沒有獲得微軟的官方支持,雖然其使用者人數(shù)很少,但是確實(shí)是“用過的都說好”,有位微軟MVP人士甚至說,這是微軟有史以來推出的最優(yōu)秀的一個(gè)framework。真是一個(gè)有趣的諷刺,最好的東西居然不被官方支持。有關(guān)于WTL的流言不少,比如這東西原本是微軟內(nèi)部專用,只是因?yàn)椴恍⌒牟疟恍孤┏鰜淼鹊?#xff0c;這更加劇它的神秘色彩
WTL安裝
從WTL主頁(http://wtl.sourceforge.net/)上可以下載到最新的WTL,解壓縮之后運(yùn)行根據(jù)你當(dāng)前安裝的VC版本選擇不同的setup.js安裝即可。注意,最新的WTL安裝程序已經(jīng)沒有VC6的安裝向?qū)Я?#xff0c;用VC6的童鞋們可以下載WTL7.1或者嘗試使用這種方法(http://hi.baidu.com/yykbrother/blog/item/cb7079caeefc0d8ec91768c9.html)。
WTL使用
這個(gè)就不用細(xì)說了,按照向?qū)?chuàng)建項(xiàng)目,然后include頭文件就行了。
WTL?核心機(jī)制
先來熟悉一下基于API的Win32應(yīng)用程序的機(jī)制。
先來看下面一段例程:
1 int APIENTRY WinMain(HINSTANCE hInstance,2 HINSTANCE hPrevInstance,
3 LPSTR lpCmdLine,
4 int nCmdShow)
5 {
6 //定義一個(gè)消息和一個(gè)加速鍵表
7 ? MSG msg;
8 HACCEL hAccelTable;
9
10 //加載存在Resource文件里的字符串
11 ? LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
12 LoadString(hInstance, IDC_WIN32TEST, szWindowClass, MAX_LOADSTRING);
13
14 //定義個(gè)窗體類
15 ? WNDCLASSEX wcex;
16 wcex.cbSize =sizeof(WNDCLASSEX);
17 wcex.style = CS_HREDRAW | CS_VREDRAW;
18 wcex.lpfnWndProc = (WNDPROC)WndProc;
19 wcex.cbClsExtra =0;
20 wcex.cbWndExtra =0;
21 wcex.hInstance = hInstance;
22 wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_WIN32TEST);
23 wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
24 wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
25 wcex.lpszMenuName = (LPCSTR)IDC_WIN32TEST;
26 wcex.lpszClassName = szWindowClass;
27 wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);
28 //注冊窗體類
29 ? RegisterClassEx(&wcex);
30
31 //創(chuàng)建主窗體
32 ? HWND hWnd;
33 hInst = hInstance; // Store instance handle in our global variable
34 ?
35 hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
36 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
37
38 if (!hWnd)
39 {
40 return FALSE;
41 }
42 //顯示窗體
43 ? ShowWindow(hWnd, nCmdShow);
44 UpdateWindow(hWnd);
45 //加載加速鍵表
46 ? hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_WIN32TEST);
47
48 //進(jìn)入主消息循環(huán)
49 ?while (GetMessage(&msg, NULL, 0, 0))
50 {
51 if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
52 {
53 TranslateMessage(&msg);
54 DispatchMessage(&msg);
55 }
56 }
57
58 return msg.wParam;
59 }
首先要為要顯示的窗體注冊一個(gè)WNDCLASS,在WNDCLASS實(shí)例中可以設(shè)置classname、style、instance、WndProc、ICON等,然后就可以以這個(gè)窗口類名創(chuàng)建一個(gè)Window(調(diào)用CreateWindow)并顯示,最后程序就進(jìn)入主消息循環(huán),分別調(diào)用GetMessage從消息隊(duì)列中取出消息,TranslateMessage將消息轉(zhuǎn)化為字符消息,DispatchMessage將消息分發(fā)到具體的窗體。
再來看WTL的典型例程:
1 //全局Module對象2 ?CAppModule _Module;
3 ?int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow)
4 {
5 ?//初始化COM
6 HRESULT hRes = ::CoInitialize(NULL);
7 ATLASSERT(SUCCEEDED(hRes));
8
9 // this resolves ATL window thunking problem when Microsoft Layer for Unicode (MSLU) is used
10 ::DefWindowProc(NULL, 0, 0, 0L);
11 // add flags to support other controls
12 AtlInitCommonControls(ICC_BAR_CLASSES);
13 //初始化Module
14 hRes = _Module.Init(NULL, hInstance);
15 ATLASSERT(SUCCEEDED(hRes));
16
17 //定義一個(gè)消息循環(huán)
18 CMessageLoop theLoop;
19 //將此線程的消息循環(huán)加入到Module中
20 _Module.AddMessageLoop(&theLoop);
21
22 //定義主窗體
23 CCusWindow wndMain;
24 //創(chuàng)建主窗體,CCusWindow類內(nèi)部必須調(diào)用DECLARE_WND_CLASS或相關(guān)宏注冊窗體類,并在Create時(shí)注冊窗體類
25 if(wndMain.Create(NULL,CWindow::rcDefault,_T("CCusWindow")) == NULL)
26 {
27 ATLTRACE(_T("Main window creation failed!\n"));
28 return0;
29 }
30 //顯示窗體
31 wndMain.ShowWindow(nCmdShow);
32 //進(jìn)入消息循環(huán)
33 int nRet = theLoop.Run();
34 //線程退出,一些清理工作
35 _Module.RemoveMessageLoop();
36
37 _Module.Term();
38
39 ::CoUninitialize();
40
41 return nRet;
42 }
下面我們對WTL的做法做詳細(xì)分解
WTL對這整個(gè)過程進(jìn)行了抽象,其中涉及到的主要類有CAppModule、CMessageLoop、CWindowImpl等
CAppModule封裝了初始化模塊,并維持了一個(gè)消息循環(huán)的map,具體定義為ATL::CSimpleMap<DWORD, CMessageLoop*>* m_pMsgLoopMap;其中DWORD為線程ID,也就是說每個(gè)線程對應(yīng)一個(gè)CMessageLoop也就對應(yīng)一個(gè)消息循環(huán)。
CMessageLoop是對消息循環(huán)的封裝。其中主要的方法為Run(),這個(gè)方法運(yùn)行后,當(dāng)前線程就進(jìn)入了主消息循環(huán),直到線程退出。當(dāng)然這里邊有很多重要的內(nèi)容,避免復(fù)雜性,這里先不說了。
CWindowImpl是WTL對窗體的封裝,大多數(shù)窗體都要繼承他。該類內(nèi)部有一個(gè)DECLARE_WND_CLASS 或者DECLARE_WND_CLASS_EX宏,這個(gè)宏會實(shí)現(xiàn)GetWndClassInfo?函數(shù),此函數(shù)創(chuàng)建一個(gè)static 類型的CWndClassInfo(對WNDCLASS的封裝)對象,并且將WNDPROC設(shè)置為StartWindowProc,在調(diào)用?CWindowImpl::Create?時(shí),會注冊此窗體類并創(chuàng)建一個(gè)新窗口。此窗體在接收到第一個(gè)消息后會自動調(diào)用StartWindowProc函數(shù)進(jìn)行消息處理,在這個(gè)函數(shù)內(nèi)部進(jìn)行了thunk的初始化并通過調(diào)用SetWindowLong將WNDPROC設(shè)為了WindowProc,這個(gè)才是真正的消息處理函數(shù)。下面是DECLARE_WND_CLASS_EX的宏定義
1 #define DECLARE_WND_CLASS_EX(WndClassName, style, bkgnd) \2 static CWndClassInfo& GetWndClassInfo() \
3 { \
4 static CWndClassInfo wc = \
5 { \
6 { style, StartWindowProc, \
7 0, 0, NULL, NULL, NULL, (HBRUSH)(bkgnd +1), NULL, WndClassName }, \
8 NULL, NULL, IDC_ARROW, TRUE, 0, _T("") \
9 }; \
10 return wc; \
11 }
CWindowImpl::Create的定義
1 template <class T, class TBase = CWindow, class TWinTraits = CControlWinTraits>2 class ATL_NO_VTABLE CWindowImpl : public CWindowImplBaseT< TBase, TWinTraits >
3 {
4 public:
5 DECLARE_WND_CLASS(NULL)
6
7 HWND Create(HWND hWndParent, RECT& rcPos, LPCTSTR szWindowName = NULL,
8 DWORD dwStyle =0, DWORD dwExStyle =0,
9 UINT nID =0, LPVOID lpCreateParam = NULL)
10 {
11 if (T::GetWndClassInfo().m_lpszOrigName == NULL)
12 T::GetWndClassInfo().m_lpszOrigName = GetWndClassName();
13 ATOM atom = T::GetWndClassInfo().Register(&m_pfnSuperWindowProc);
14
15 dwStyle = T::GetWndStyle(dwStyle);
16 dwExStyle = T::GetWndExStyle(dwExStyle);
17
18 return CWindowImplBaseT< TBase, TWinTraits >::Create(hWndParent, rcPos, szWindowName,
19 dwStyle, dwExStyle, nID, atom, lpCreateParam);
20 }
21 };
可以看出WTL只是對WIN32 API做了封裝,具體的調(diào)用過程沒有任何改變,因此如果有一些WIN32 API編程的底子,直接看源代碼學(xué)習(xí)WTL應(yīng)該不是難事。
轉(zhuǎn)載于:https://www.cnblogs.com/JczmDeveloper/p/3480366.html
總結(jié)
- 上一篇: 2、Linux基础练习题
- 下一篇: 1349 - View's SELECT