win32程序值窗口程序,以及消息機制
一丶簡介
通過上一講.我們了解了窗口其實是繪制出來的.而且是不斷繪制的過程. 所以窗口的本質是繪制. 但是我們現在看到的窗口程序.都可以點擊關閉按鈕. 使用鼠標點擊會有反應.
而我們要怎么實現那.
其實鼠標點擊是產生了一個消息. window把這個消息封裝成了消息結構體. 發送給了我們的窗口程序. 那么windows怎么知道你點擊的那個窗口那?
是這樣的. 當我們點擊的時候. 會記錄點擊坐標.消息.等等. windows系統會接受到. 然后遍歷內核中的WINOBJ結構. 而這個結構中存儲着窗口對象. 窗口對象對應着消息線程.
所以windows一層一層的遍歷.則找到了對應的窗口以及窗口對應的線程.然后發送給我們的應用程序.
上面說的我們需要了解. 要知道消息怎么產生的. 怎么傳遞的.那么下面編程就明白了.
例如下圖:
每個應用程序都有一個線程對象. 而這個線程對象如果創建窗口.那么內核中就有這個窗口對象.
如果我們有鼠標點擊的消息.鍵盤消息等等.操作系統都會遍歷窗口對象. 而窗口對象也會保存着創建這個窗口對象對應的線程對象. 而這個線程對象中則有消息隊列.
這樣的話操作系統則會封裝消息發送給我們窗口對象.
二丶Wind窗口類結構.創建窗口程序.
1.進行窗口編程需要注意的問題
在Windows中進行窗口編程.入口點已經改成WinMain了. 有四個參數.
如以下代碼所示
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, //窗口的實例句柄 hinstance代表模塊意思 HWND代表窗口意思. HANDLE代表內核對象. HDC 設備上下文. _In_opt_ HINSTANCE hPrevInstance, //父窗口句柄.不需要. _In_ LPWSTR lpCmdLine, //命令行參數 _In_ int nCmdShow) //命令. 最大化命令.還是最小化命令. { return 0; }
wWinMain 因為有UNICODE跟ASCII區別. 所以我是UNICODE使用wWinMain. A版本就是用WinMain
2.進行Windows編程的調試手法
在Windows中我們調試程序不能簡單的使用printf進行調試.或者打印輸出了. 我們可以使用兩個API進行操作.
1.Sprintf() 格式化字符串.
2.OutPutDebugString() 輸出調試字符串.
具體兩個API. 不再累贅.百度搜索即可.
因為OutPutDebugString() 只能打印固定字符串.所以使用sprintf進行格式化字符串.如下面代碼.
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { TCHAR str[30] = { NULL }; wsprintf(str,TEXT( "%s"),TEXT("HelloWin32")); //因為是Unicode所以使用W版本. OutputDebugStringW(str); return 0; }
我們編譯出程序之后.可以使用DebugView這款工具查看.
3.窗口編程的步驟
1.創建窗口類. windows提供的窗口樣式.我們需要給定.
2.注冊窗口類.創建了窗口我們需要注冊到windows系統中.
3.創建窗口.如果注冊窗口成功.那么我們需要創建出來這個窗口.並且顯示跟更新.
4.消息處理
4.窗口編程需要的主要結構
窗口的創建Windows已經為我們提供了. 這個結構就是WNDCLASSEXW 結構
看下這個結構中的內容吧
typedef struct _WNDCLASSEX { UINT cbSize; 擴展的大小. 自己WndClass本身大小. UINT style; 風格 WNDPROC lpfnWndProc; 窗口回調.消息都要進入這個回調 int cbClsExtra; int cbWndExtra; HINSTANCE hInstance; 實例句柄 HICON hIcon; 圖標 HCURSOR hCursor; 光標 HBRUSH hbrBackground; 背景 LPCTSTR lpszMenuName; 菜單名稱 LPCTSTR lpszClassName; 類名稱 HICON hIconSm; 最小化圖標 } WNDCLASSEX, *PWNDCLASSEX;
這個結構就是說.你的窗口是什么樣式. 大小.是否有圖標. 消息處理函數在哪里等等.需要我們給指定.
5.完整代碼.
// WindoS.cpp : 定義應用程序的入口點。 // #include "stdafx.h" #include <stdio.h> #include <stdlib.h> #include "WindoS.h" #define MAX_LOADSTRING 100 // 全局變量: HINSTANCE hInst; // 當前實例 WCHAR szTitle[MAX_LOADSTRING] = TEXT("第一個我的窗口"); // 標題欄文本 WCHAR szWindowClass[MAX_LOADSTRING] = TEXT("MyWindow"); // 主窗口類名 // 此代碼模塊中包含的函數的前向聲明: LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { //1.自定義窗口樣式 WNDCLASS wcex; wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; //設置回調 wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; //設置模塊實例句柄 wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOS)); wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); //設置背景顏色 wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WINDOS); wcex.lpszClassName = szWindowClass; //設置類名
//上面主要就是4個參數有用. 回調函數 背景顏色 模塊句柄. 類名 //2.注冊窗口類 BOOL bRet = RegisterClass(&wcex); //A RegisterClass U RegisterClassW 擴展 RegisterClassExA /ExW if (bRet == FALSE) { return 0; } //3.創建窗口 並且顯示跟更新窗口 HWND hWnd = CreateWindowW( szWindowClass, //我們的類名 szTitle, //我們自定義的窗口名稱 WS_OVERLAPPEDWINDOW, //窗口的創建樣式 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, //實例句柄 nullptr); //上面重要的也就是4個參數.其余參數查詢下MSDN. if (!hWnd) { return FALSE; } ShowWindow(hWnd, SW_SHOW); UpdateWindow(hWnd); //4.消息循環. MSG msg; /* 1參數是消息結構體.操作系統會往里面填寫消息. 2 參數窗口句柄 因為每個線程可以有多個窗口.表示我要取那個窗口的消息 3.4 參數表示我要取這個窗口的那個消息. 后面三個參數屬於過濾條件 */ while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) { if (bRet == -1) { // handle the error and possibly exit } else { TranslateMessage(&msg); //鍵盤消息轉換為小寫. DispatchMessage(&msg); //分發消息.將我們的消息傳遞給我們的回調函數處理. 重要函數.此消息會將Windows的消息.發送給我們 定義窗口類的時候給的回調函數.這樣我們就可以根據消息執行我們代碼了. } } return 0; } // // 函數: WndProc(HWND, UINT, WPARAM, LPARAM) // // 目的: 處理主窗口的消息。 // // WM_COMMAND - 處理應用程序菜單 // WM_PAINT - 繪制主窗口 // WM_DESTROY - 發送退出消息並返回 // // 我們的窗口回調. LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_COMMAND: //菜單消息類型 { int wmId = LOWORD(wParam); //取低兩位為菜單ID.根據菜單ID可以進行操作我們的窗口 // 分析菜單選擇: switch (wmId) { case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); //如果不處理.則必須調用這個函數教給默認的窗口回調處理 } } break; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); // TODO: 在此處添加使用 hdc 的任何繪圖代碼... LineTo(hdc, 0, 100); EndPaint(hWnd, &ps); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }
創建完畢的結果
三丶消息類型
我們回調中有我們的消息類型.我們可以判斷消息類型進行我們不同的操作.
比如菜單消息.
WM_COMMAND. 如果是這個消息.那么回調函數的 wparam等附加信息就是WM_COMMAND的附加消息了. 我們可以取低位得出操作的菜單ID.進而進行消息處理.
WM_PAINT 這個消息是繪制的消息.我們知道.窗口是不斷繪制的.所以繪制消息會一直來.
WM_DESTROY 窗口關閉消息. 如果接受到這個消息.則調用API往消息隊列中(MSG)中傳遞退出消息. 此時外層主線程就會結束.
具體API:
postQuitMessage(0);
當前具體的消息還要查詢MSDN. 因為消息種類很多.
windows消息都是WM開頭的.
比如查詢WM_COMMAND消息
可以清楚的看到.她會告訴你如果是WM_COMMAND消息來了.那么回調函數的參數.分別代表的是什么意思.