深入剖析WTL——Win32模型

  <script src="http://wz.csdn.net/javascripts/vote.js" type="text/javascript"></script>
WTL 是Windows Template Library的縮寫。最初,WTL是由微軟的ATL(Active Template Library)小組成員開發的一個SDK例子。主要是基於ATL的對Win32 API的封裝。從2.0後,功能逐步完善,成爲了一個完整的支持窗口的框架(windows framework)。

與MFC相比較,功能並沒有MFC完善。比如MFC支持doc/view架構,而WTL並不支持。同時,WTL也沒有Microsoft的官方支持。但是,WTL是基於模版(template)的,其應用程序最小隻有24KB,同時不象MFC,依賴DLL(MFC需要MFC42.DLL)。

WTL系列文章對WTL進行了深入剖析,希望能方便您對WTL有一個深入的理解,從而能得心應手的開發出高質量的Windows應用程序。



Win32的線程模型


爲了便於以後的探討,首先看一下Win32的線程模型。

一個Win32應用程序(或進程)是由一個或多個併發的線程組成的,其中第一個啓動的線程稱爲主線程。

Win32定義了兩種類型的線程,界面線程和工作線程。Win32的每個進程可以有一個或多個界面線程和/或多個工作線程。界面線程擁有一個或多個窗口,擁有一個消息隊列和其它屬於界面線程的元素。工作線程就是一般的線程,它沒有窗口,沒有消息隊列。

界面線程通常有一個或幾個窗口。當某一個窗口有消息時,界面線程會調用相應的窗口函數(Windows Process)來處理該事件。由於某消息循環由它界面線程處理,同時不必在乎是哪個線程發送消息的,因此,Windows會保證線程間的同步問題。

對於工作線程,線程間的同步必須由程序員來實現。儘可能避免死鎖和競爭出現。

Win32應用程序模型


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()將該消息作爲參數調用對應的窗口的窗口函數,這就是分發的實質)。
發佈了3 篇原創文章 · 獲贊 0 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章