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訊息來了.那麼回呼函數的參數.分別代表的是什麼意思.