MFC底層窗口實現
- 2019 年 10 月 18 日
- 筆記
簡要說明
MFC是微軟的一個基礎類庫,如果在Windows平台上做GUI的開發,這是一個不錯的選擇。簡單的記錄MFC學習過程中的需要掌握或者後期需要查看的知識點。
Windows消息機制
- 作業系統首先捕獲到來自鍵盤或滑鼠等輸入系統的消息,並將獲取到的消息存放到消息隊列中。
- 應用程式一直通過GetMessage()從消息隊列中獲取消息。
- 應用程式再將獲取到的消息通過DispatchMessage()分派到作業系統
- 作業系統再執行「窗口過程」
Windows編程模型
- WinMain函數的定義(WinMain函數是Windows程式的入口)
- 創建一個窗口
- 進行消息循環
- 編寫窗口過程函數
整體框架結構
實現步驟
創建win32項目
- 新建win32項目,選擇「空項目」並完成創建。
- 添加源文件,文件名以「.c」結尾。
- 添加頭文件「windows.h」
- 添加程式入口函數「WindowMain」
//WINAPI 代表__stdcall 參數的傳遞順序:從右到左 以此入棧,並且在函數返回前 清空堆棧 int WINAPI WinMain( HINSTANCE hInstance, // 應用程式實例句柄 HINSTANCE hPrevInstance, // 上一個應用程式句柄,在win32環境下,參數一般為NULL,不起作用了 LPSTR lpCmdLine, // char * argv[] int nShowCmd) // 顯示命令 最大化、最小化 正常 { ... return 0; }
應用程式過程
- 設計窗口
- 首先實例化一個窗口類,再依次設置其參數
WNDCLASS wc; wc.cbClsExtra = 0; //類的額外的記憶體 wc.cbWndExtra = 0; //窗口額外的記憶體 wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); // 設置背景 wc.hCursor = LoadCursor(NULL, IDC_HAND); //設置游標 如果第一個參數為NULL,代表使用體統提供的游標 wc.hIcon = LoadIcon(NULL, IDI_ERROR); wc.hInstance = hInstance; //應用程式實例句柄 傳入WinMain中的形參即可 wc.lpfnWndProc = WindowProc;// 回調函數 窗口過程 wc.lpszClassName = TEXT("WIN");//指定窗口類名稱 wc.lpszMenuName = NULL; //NULL代表不使用菜單 wc.style = 0; //顯示風格 0為默認
- 註冊窗口
- 將上述實例化的窗口類進行註冊
RegisterClass(&wc);
- 創建窗口
/* lpClassName, 類名 lpWindowName, 標題名 dwStyle, 風格 WS_OVERLAPPEDWINDOW x, 顯示坐標 CW_USEDEFAULT y, nWidth, 寬 nHeight, 高 hWndParent, 父窗口 hMenu, 菜單 hInstance, 實例句柄 lpParam 附加值 lp一般為滑鼠附加值 NULL */ HWND hwnd = CreateWindow(wc.lpszClassName, TEXT("WINDOWS"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, wc.hInstance, NULL);
- 顯示和更新
ShowWindow(hwnd, SW_SHOWNORMAL); UpdateWindow(hwnd);
- 通過循環取消息
/* HWND hwnd; 主窗口句柄 UINT message; 具體消息名稱 WPARAM wParam; 附加消息 鍵盤消息 LPARAM lParam; 附加消息 滑鼠消息 DWORD time; 消息產生時間 POINT pt; 附加消息 滑鼠消息 x y */ MSG msg; while (1) { /* LPMSG lpMsg, 消息 HWND hWnd, 捕獲窗口 填NULL代表捕獲所有的窗口 UINT wMsgFilterMin, 最小和最大的過濾的消息 一般填入0 UINT wMsgFilterMax 填0代表捕獲所有消息 */ if (GetMessage(&msg, NULL, 0, 0) == FALSE) { break; } // 翻譯消息 TranslateMessage(&msg); // 分發消息 DispatchMessage(&msg); }
- 消息處理(窗口過程)
//CALLBACK 代表__stdcall 參數的傳遞順序:從右到左 以此入棧,並且在函數返回前 清空堆棧 LRESULT CALLBACK WindowProc( HWND hwnd, // 消息所屬的窗口句柄 UINT uMsg, // 具體消息名稱 WM_XXXXXXXX 消息名 WPARAM wParam, // 鍵盤附加消息 LPARAM lParam // 滑鼠附加消息 ) { switch (uMsg) { case WM_CLOSE: //所有xxxWindow為結尾的方法,都不會進入到消息隊列中,而是直接執行 DestroyWindow(hwnd); //DestroyWindow 發送另一個消息 WM_DESTROY break; case WM_DESTROY: PostQuitMessage(0); break; case WM_LBUTTONDOWN: { int xPos = LOWORD(lParam); int yPos = HIWORD(lParam); char buf[1024]; wsprintf(buf, TEXT("x = %d, y = %d"), xPos, yPos); MessageBox(hwnd, buf, TEXT("滑鼠左鍵按下"), MB_OK); break; } case WM_KEYDOWN: MessageBox(hwnd, TEXT("鍵盤按下"), TEXT("鍵盤按下"), MB_OK); break; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, &ps); TextOut(hdc, 100, 100, TEXT("HELLO"), strlen("HELLO")); EndPaint(hwnd, &ps); break; } default: break; }
大概流程如下
小結
- 首先要對整個創建流程有個整體的把握
- 學會查看msdn文檔,不知道的參數及方法都可以從中獲取
- 學習很重要,要好好學習