與MFC相比較,功能並沒有MFC完善。比如MFC支持doc/view架構,而WTL並不支持。同時,WTL也沒有Microsoft的官方支持。但是,WTL是基於模版(template)的,其應用程序最小隻有24KB,同時不象MFC,依賴DLL(MFC需要MFC42.DLL)。
WTL系列文章對WTL進行了深入剖析,希望能方便您對WTL有一個深入的理解,從而能得心應手的開發出高質量的Windows應用程序。
爲了便於以後的探討,首先看一下Win32的線程模型。
一個Win32應用程序(或進程)是由一個或多個併發的線程組成的,其中第一個啓動的線程稱爲主線程。
Win32定義了兩種類型的線程,界面線程和工作線程。Win32的每個進程可以有一個或多個界面線程和/或多個工作線程。界面線程擁有一個或多個窗口,擁有一個消息隊列和其它屬於界面線程的元素。工作線程就是一般的線程,它沒有窗口,沒有消息隊列。
界面線程通常有一個或幾個窗口。當某一個窗口有消息時,界面線程會調用相應的窗口函數(Windows Process)來處理該事件。由於某消息循環由它界面線程處理,同時不必在乎是哪個線程發送消息的,因此,Windows會保證線程間的同步問題。
對於工作線程,線程間的同步必須由程序員來實現。儘可能避免死鎖和競爭出現。
Win32應用程序可以分成兩大類:控制檯程序(console application)和窗口界面程序(windows GUI application)。控制檯程序的入口函數是main(),窗口界面程序的入口函數是WinMain()。
入口函數就是程序的主線程的運行起點。
這裏討論的開發框架(Framework)是針對窗口界面程序的。
窗口界面程序通常分成以下幾類:SDI, MDI, multi-SDI, 和Dialog應用程序。
SDI(Single Document Interface)應用程序通常只有一個主窗口(通常是一個框架窗口,Frame Window)。框架窗口包含菜單、工具欄、狀態欄和稱爲視(View)的客戶工作區。
multi-SDI(Multiple Threads SDI)應用程序有框架個主窗口。比如IE瀏覽器,使用"文件/新建/窗口"命令後,會出現另一個IE窗口。
MDI(Multiple Document Interface)應用程序有一個主框架窗口,但有多個子框架窗口。每個子窗口都有自己的視(View)。
Dialog應用程序是基於對話框的。
通常一個簡單的SDI應用程序由兩個函數組成。一個是應用程序入口函數WinMain(),另一個是該應用程序窗口的窗口函數。
程序(主線程)從入口函數開始運行。在該函數中,首先是註冊並創建一個主窗口。然後,啓動消息循環。消息循環中,根據不同的消息,將消息發送到窗口函數中處理。當消息是退出消息時,該入口函數會退出消息循環,然後結束程序。
下面是一個最簡單的Windows界面應用程序。
//應用程序入口函數int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){ MSG msg; //1. 註冊窗口類 WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = (WNDPROC)WndProc; //指定窗口函數 wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_HELLOWORLD); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = (LPCSTR)IDC_HELLOWORLD; wcex.lpszClassName = szWindowClass;wcex.hIconSm = LoadIcon(wcex.hInstance,(LPCTSTR)IDI_SMALL); RegisterClassEx(&wcex); //2. 創建一個該類型的窗口 HWND hWnd; hWnd = CreateWindow(szWindowClass,szTitle,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); if (!hWnd) return FALSE; //3. 按nCmdShow所指定的方式顯示窗口 ShowWindow(hWnd, nCmdShow);UpdateWindow(hWnd); //4. 啓動消息循環,將消息發送給相應的窗口函數 while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam;}//窗口函數LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){ PAINTSTRUCT ps; HDC hdc; char* szHello = "Hello, world!"; switch (message) { case WM_PAINT: hdc = BeginPaint(hWnd, &ps); RECT rt; GetClientRect(hWnd, &rt); DrawText(hdc, szHello, strlen(szHello),&rt,DT_CENTER); EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); //退出應用程序 break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } |
上面程序的執行過程如下:
1、註冊窗口類
在使用CreateWindwo()或CreateWindowEx()創建窗口時,必須提供一個標識窗口類的字符串。該窗口類定義了一些窗口的基本屬性。其中一個重要的工作是向操作系統提供窗口函數。該函數是回調函數,用於處理髮送給該窗口的消息。
在上面程序中,僅僅簡單的處理了兩個消息。一個是向窗口區域畫出"Hello world."字符串。另一個是當窗口撤消時,嚮應用程序發送"退出應用程序"消息。
2、創建窗口
3、顯示窗口
4、啓動消息循環,分發並處理消息。
while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } |
在上述消息循環代碼中,調用GetMessage()從線程的消息隊列中取出一條消息。如果消息爲0(由窗口函數處理"WM_DESTROY"消息時發送的"PostQuitMessage(0)"),會退出消息循環。
然後調用TranslateMessage()翻譯消息。
翻譯後,再調用DispatchMessage()將該消息分發至相應的窗口函數進行處理。 (實際上DispatchMessage()將該消息作爲參數調用對應的窗口的窗口函數,這就是分發的實質)。