Windows 程序分爲「程序代碼」和「UI資源」兩大部分,經過RC編譯器整合爲一個完整的EXE 文件。程序員
所謂UI 資源是指功能菜單、對話框外貌、程序圖標、光標形狀等等東西。編程
這些UI 資源的實際內容(二進制代碼)系藉助各類工具產生,並以各類擴展名存在,如.ico、.bmp、.cur 等等。程序員必須在一個所謂的資源描述檔(.rc)中描述它們。windows
RC 編譯器(RC.EXE)讀取RC 檔的描述後將全部UI資源檔集中製做出一個.RES 檔,再與程序代碼結合在一塊兒,這纔是一個完整的Windows可執行文件。數據結構
Windows 程序的進行系依靠外部發生的事件來驅動。換句話說,程序不斷等待(利用一個while 迴路),等待任何可能的輸入,而後作判斷,而後再作適當的處理。上述的「輸入」是由操做系統捕捉到以後,以消息形式(一種數據結構)進入程序之中。函數
1.程序初始化過程當中調用CreateWindow,爲程序創建了一個窗口,做爲程序的屏幕舞臺。CreateWindow產生窗口以後會送出 wM_CREATE直接給窗口函數,後者因而能夠在此時作些初始化操做(例如配置內存、打開文件、讀初始數據……)。工具
2在程序活着的過程當中,不斷以 GetMessage從消息隊列中抓取消息。若是這個消息是WM_oUIT,GetMessage會傳回0而結束while循環,進而結束整個程序。開發工具
3.DispatchMessage經過Windows USER模塊的協助與監督,把消息分派至窗口函數。消息將在該處被判別並處理。ui
4.程序不斷進行第2步和第3步的操做。spa
5.當使用者按下系統菜單中的Close命令項時,系統送出WM_CLOSE。一般程序的窗口函數不攔截此消息,因而 DefWindowProc處理它。操作系統
6.DefWindowProc收到 WM_CLOSE後,調用 DestroyWindow把窗口清除。Destroy Window自己又會送出WM_DESTROY。
7.程序對WM_DESTROY的標準反應是調用PostQuitMessage。
8.PostQuitMessage沒什麼其它操做,就只送出 WM_QUIT 消息,準備讓消息循環中的GetMessage取得,如步驟2,結束消息循環。
Windows的三大核心繫統:負責窗口對象產生和消息分發的USER模塊,負責圖像顯示繪製的GDI模塊,負責內存、進程、IO管理的KERNEL模塊。
試想象一下如何在一個像素陣列上產生窗口對象,其實就是使用GDI繪製窗口,不停的以必定的頻率刷新顯示在屏幕上,這就是圖形界面,若是由在DOS或Windows DOS模擬器下編寫圖形界面的經驗這個比較好理解。因此說其實USER模塊中的窗口產生是依靠GDI模塊的(包括菜單、滾動條等都是使用GDI來繪製的)。
那麼,下面咱們就從USER模塊和GDI模塊來講說Windows 的窗體原理。
若是接觸過Win32 SDK編程的知道一個標準Windows窗體的產生過程:
貼上一個標準Windows窗體的產生代碼:
#include <windows.h> LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("窗口類名稱"); HWND hwnd; MSG msg; WNDCLASSEX wndclassex = {0}; //設計窗口類 wndclassex.cbSize = sizeof(WNDCLASSEX); wndclassex.style = CS_HREDRAW | CS_VREDRAW; wndclassex.lpfnWndProc = WndProc; wndclassex.cbClsExtra = 0; wndclassex.cbWndExtra = 0; wndclassex.hInstance = hInstance; wndclassex.hIcon = LoadIcon (NULL, IDI_APPLICATION); wndclassex.hCursor = LoadCursor (NULL, IDC_ARROW); wndclassex.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH); wndclassex.lpszMenuName = NULL; wndclassex.lpszClassName = szAppName; wndclassex.hIconSm = wndclassex.hIcon; //註冊窗口類 if (!RegisterClassEx (&wndclassex)) { MessageBox (NULL, TEXT ("RegisterClassEx failed!"), szAppName, MB_ICONERROR); return 0; } //產生窗口 hwnd = CreateWindowEx (WS_EX_OVERLAPPEDWINDOW, szAppName, TEXT ("窗口名稱"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); //顯示窗口 ShowWindow (hwnd, iCmdShow); UpdateWindow (hwnd); //啓動消息循環泵循環獲取消息分配到窗體過程函數處理 while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg); DispatchMessage (&msg); } return msg.wParam; } //窗體過程函數 LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; switch (message) { case WM_CREATE: return (0); case WM_PAINT: hdc = BeginPaint (hwnd, &ps); EndPaint (hwnd, &ps); return (0); case WM_DESTROY: PostQuitMessage (0); return (0); } return DefWindowProc (hwnd, message, wParam, lParam); }
須要明白的是,全部Windows的窗體及控件歸根結底都是使用CreateWindow或CreateWindowEx來建立的,他們都須要標準Windows窗體的產生過程。
普通的窗體好理解,主要須要弄清楚是對話框及控件的產生和消息分派處理流程。
對話框及其子控件的管理依靠Windows內建的對話框管理器,對話框管理器的工做包括:
1.根據咱們在資源設計器中設計的對話框及子控件產生的.rc文件來自動生成對話框和子控件(若是有手動編寫.rc文件的經歷的話,知道編寫RC文件其實就是指定窗口和子控件大小、類型、樣式等參數,對話框管理器將這些參數傳入CreateWindow函數產生窗體)
2.模態對話框直接顯示窗體,非模態對話框消息指明WS_VISIBLE屬性的話,須要調用ShowWindow來顯示窗體。
3.維護一個消息循環泵,對於模態對話框來講這個消息泵的消息不通過父窗口,因此表現爲模態;對於非模態對話框這個消息泵消息通過主窗口,必須由主窗口傳給非模態對話框,表現爲非模態。
4.維護一個內建的窗體過程函數,對於對話框來講會處理對話框的關閉打開及子窗口的焦點、tab等,對於子控件也是同樣,每一個子控件會有本身類型的窗體過程函數,窗體過程函數處理子控件的得到或失去焦點、按下或彈起、建立等表現樣式和行爲。
對於對話框來講,他會開放一個對話框過程函數,讓部分消息先經過對話框管理函數處理,若是對話框過程函數不處理才交給默認的內建過程函數處理,對於子控件來講,他們並無開放過程函數,而是由內建窗體函數將要處理的消息發給父窗口處理。
那麼對話框管理器完成了標準Windows窗體的產生中後半部分工做,至於設計窗口類和註冊窗口類這是由Windows本身預先作好了的,如常見的「button」、「listbox」、「edit」類等等。
那麼既然全部的窗體(包括對話框和控件)產生過程同樣,那麼咱們就能夠將對話框管理器的部分工做替換掉:
1.不使用對話框讀取.rc模板的方式,直接將參數傳遞給CreateWindow函數來建立對話框和控件,這就是常見的動態建立控件原理。
2.設置控件自繪製如BS_OWNDRAW屬性,開放控件的WM_DRAWITEM消息給父窗口,由父窗口來繪製按鈕樣式,這就是常見的控件重繪原理。
3.替換內建的窗體函數,將消息傳到自定義的窗體過程函數處理,這就是常見的控件子類化原理。
須要Windows操做系統和開發工具的小夥伴,能夠加羣免費領取噢~