MFC底層窗口實現

  • 2019 年 10 月 18 日
  • 筆記

簡要說明

MFC是微軟的一個基礎類庫,如果在Windows平台上做GUI的開發,這是一個不錯的選擇。簡單的記錄MFC學習過程中的需要掌握或者後期需要查看的知識點。

Windows消息機制

  1. 作業系統首先捕獲到來自鍵盤或滑鼠等輸入系統的消息,並將獲取到的消息存放到消息隊列中。
  2. 應用程式一直通過GetMessage()從消息隊列中獲取消息。
  3. 應用程式再將獲取到的消息通過DispatchMessage()分派到作業系統
  4. 作業系統再執行「窗口過程」

Windows編程模型

  1. WinMain函數的定義(WinMain函數是Windows程式的入口)
  2. 創建一個窗口
  3. 進行消息循環
  4. 編寫窗口過程函數

整體框架結構

實現步驟

創建win32項目

  1. 新建win32項目,選擇「空項目」並完成創建。
  2. 添加源文件,文件名以「.c」結尾。
  3. 添加頭文件「windows.h」
  4. 添加程式入口函數「WindowMain」
//WINAPI 代表__stdcall 參數的傳遞順序:從右到左 以此入棧,並且在函數返回前 清空堆棧  int WINAPI WinMain(      HINSTANCE hInstance,      // 應用程式實例句柄      HINSTANCE hPrevInstance,  // 上一個應用程式句柄,在win32環境下,參數一般為NULL,不起作用了      LPSTR lpCmdLine,          // char * argv[]      int nShowCmd)             // 顯示命令 最大化、最小化 正常  {      ...          return 0;  }

應用程式過程

  1. 設計窗口
  • 首先實例化一個窗口類,再依次設置其參數
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為默認
  1. 註冊窗口
  • 將上述實例化的窗口類進行註冊
RegisterClass(&wc);
  1. 創建窗口
/*  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);  
  1. 顯示和更新
ShowWindow(hwnd, SW_SHOWNORMAL);  UpdateWindow(hwnd);
  1. 通過循環取消息
/*  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);  }
  1. 消息處理(窗口過程)
//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;  }

大概流程如下

小結

  1. 首先要對整個創建流程有個整體的把握
  2. 學會查看msdn文檔,不知道的參數及方法都可以從中獲取
  3. 學習很重要,要好好學習