Windows 程序分為「程序代碼」和「UI資源」兩大部分,通過RC編譯器整合為一個完整的EXE 文件。
所謂UI 資源是指功能菜單、對話框外貌、程序圖标、光标形狀等等東西。
這些UI 資源的實際内容(二進制代碼)系借助各種工具産生,并以各種擴展名存在,如.ico、.bmp、.cur 等等。程序員必須在一個所謂的資源描述檔(.rc)中描述它們。
RC 編譯器(RC.EXE)讀取RC 檔的描述後将所有UI資源檔集中制作出一個.RES 檔,再與程序代碼結合在一起,這才是一個完整的Windows可執行文件。
2、Windows程序與操作系統之間的關系Windows 程序的進行系依靠外部發生的事件來驅動。換句話說,程序不斷等待(利用一個while 回路),等待任何可能的輸入,然後做判斷,然後再做适當的處理。上述的「輸入」是由操作系統捕捉到之後,以消息形式(一種數據結構)進入程序之中。
3、Windows窗口生命周期如下:
1.程序初始化過程中調用CreateWindow,為程序建立了一個窗口,作為程序的屏幕舞台。CreateWindow産生窗口之後會送出 wM_CREATE直接給窗口函數,後者于是可以在此時做些初始化操作(例如配置内存、打開文件、讀初始數據……)。
2在程序活着的過程中,不斷以 Getmessage從消息隊列中抓取消息。如果這個消息是WM_oUIT,GetMessage會傳回0而結束while循環,進而結束整個程序。
3.DispatchMessage通過Windows USER模塊的協助與監督,把消息分派至窗口函數。消息将在該處被判别并處理。
4.程序不斷進行第2步和第3步的操作。
5.當使用者按下系統菜單中的Close命令項時,系統送出WM_CLOSE。通常程序的窗口函數不攔截此消息,于是 DefWindowProc處理它。
6.DefWindowProc收到 WM_CLOSE後,調用 DestroyWindow把窗口清除。Destroy Window本身又會送出WM_DESTROY。
7.程序對WM_DESTROY的标準反應是調用PostQuitMessage。
8.PostQuitMessage沒什麼其它操作,就隻送出 WM_QUIT 消息,準備讓消息循環中的GetMessage取得,如步驟2,結束消息循環。
3.Windows窗體原理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操作系統和開發工具的小夥伴,可以點擊下方了解更多免費領取噢~
下一節講:MFC對話框原理
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!