標籤:windows win32
Win32訊息機制 過程驅動:程式是按照我們預先定義好的順序執行,每執行一步,下一步都已經按照預定的順序 繼續執行,直至程式結束。 事件驅動:程式的執行順序是無序的。某個時間點所執行的代碼,是由外界 通知。由於我們無法決定程式執行順序。所以代碼的執行也是無序的。 Win32基本訊息 WM_DESTROY:
視窗銷毀時的訊息,可以做退出或善後處理 WM_CREATE:
視窗建立訊息,是在視窗建立後,視窗處理函數收到的第一條訊息
可以在這個訊息內,做初始化或者穿件子視窗
WPARAM wParam - 不使用
LPARAM lParam - CREATESTRUCT指標
WM_SIZE:
當視窗大小發生改變時,會收到這個訊息。
可以在這個訊息中調整視窗的布局
WM_SYSCOMMAND:
系統命令訊息,當點擊系統功能表和按鈕時會收到
可以在這個訊息中,提示使用者儲存資料等
WM_PAINT:
繪圖訊息
鍵盤訊息:
滑鼠訊息
WM_TIME:定時器訊息 訊息的擷取和發送
擷取GetMessage/PeekMessage
GetMessage 擷取訊息,阻塞函數
PeekMessage 擷取訊息,非阻塞函數
發送SendMessage/PostMessage
SendMessage 發送訊息並等候訊息處理結束才返回。
PostMessage 發送訊息後立即返回,不關心訊息處理的結果。
LRESULT SendMessage/PostMessage( HWND hWnd, //處理訊息視窗 UINT Msg, //訊息的ID WPARAM wParam, //訊息的參數 LPARAM lParam );//訊息的參數
3 訊息組成和分類
3.1 訊息組成
視窗控制代碼/訊息ID/訊息參數(WPARAM.LPARAM)
3.2 訊息分類
3.2.1 系統訊息 - 由系統定義和使用的訊息
例如:WM_CREATE/WM_SIZE
訊息ID範圍為: 0 - 0x03FF(WM_USER-1)
3.2.2 使用者定義訊息 - 應用程式可以自己定義和使用的訊息, WM_USER(0x0400)
從WM_USER的ID開始,到0x7FFF,是使用者可以定義使用的訊息.
3.2.3 其他訊息範圍
WM_APP(0x8000)-0xBFFF:應用程式訪問視窗的訊息ID
0xC000-0xFFFF: 應用程式訪問訊息,使用字串註冊系統產生相應訊息ID
3.2.4 使用者定義訊息的使用
1)定義自訂訊息ID:
#define WM_FIRSTMSG (WM_USER+1)
2)在視窗處理函數中,響應訊息
switch( nMsg ) { case WM_FIRSTMSG: //處理函數 break; }
3)SendMessage/PostMessage發送訊息
SendMessage( hWnd, WM_FIRSTMSG, 0, 0 );
4 訊息佇列
4.1 訊息佇列 - 用於儲存訊息的記憶體空間,訊息在隊列中是先入先出.
4.2 訊息佇列的分類
4.2.1 系統訊息佇列 - 由系統維護的訊息佇列.
4.2.2 應用程式訊息佇列(線程訊息對列) -屬於每個線程的各自擁有的訊息佇列.
5 訊息和訊息佇列
5.1 根據訊息和訊息佇列關係,將訊息分成兩種:
隊列訊息 - 可以存放在訊息佇列中的訊息.
非隊列訊息 - 發送時不進入訊息佇列.
5.2 隊列訊息
首先存放到訊息佇列當中,然後由GetMessage/PeekMessage取出,然後進行處理.
例如: 滑鼠訊息/鍵盤訊息/WM_PAINT/WM_QUIT/M_TIMER訊息
5.3 非隊列訊息
訊息發送直接發送給指定的視窗,尋找視窗的處理函數,返回處理結果.
6 訊息的擷取
6.1 訊息迴圈
6.1.1 GetMesssage從隊列中擷取訊息,判斷是否是WM_QUIT訊息,如果發現是WM_QUIT訊息,訊息迴圈結束,否則繼續下一步.
6.1.2 TranslateMessage 翻譯按鍵訊息,如果發現有按鍵訊息,產生字元訊息放入訊息對列, 繼續下一步
6.1.3 DispatchMessage 找到訊息所發視窗的處理函數,處理訊息.處理完成後,返回6.1.1.
6.2 GetMesssage和PeekMessage
6.2.1 從線程訊息佇列中擷取訊息,如果找到訊息,就返回訊息,進行訊息處理. 如果未找到訊息,執行6.2.2
6.2.2 尋找系統訊息佇列.通過向系統訊息佇列查詢,如果找到訊息,擷取訊息並返回,進行訊息處理.如果未找到訊息,執行6.2.3
6.2.3 檢查視窗需要重新繪製的範圍,如果發現存在重新繪製的範圍,會產生WM_PAINT訊息.然後進行訊息處理, 如果未找,執行6.2.4
6.2.4 檢查WM_TIMER定時器訊息,如果發現存在已經到時的定時器,會產生WM_TIMER訊息.進行訊息處理. 如果未找,執行6.2.5
6.2.5 執行記憶體管理工作.
6.2.6 根據函數不同,處理結果不同:
GetMesssage - 阻塞,等候下一條訊息
PeekMessage - 讓出控制權,交給後面的代碼執行.
7 訊息發送
7.1 訊息發送分兩種
發送(Send)訊息 - 直接發送給指定的視窗,並等候結果.
投遞(Post)訊息 - 發送到訊息佇列當中,立刻返回,由訊息迴圈處理.
7.2 PostMessage和SendMessage
PostMessage產生隊列訊息,由於發送後不等候訊息處理結果,所以不能確定訊息是否被處理成功.
SendMessage產生非隊列訊息,可以確定訊息是否成功.
看下面的程式碼範例:
/*File : message.cpp *Auth : sjin *Date : 20140519 *Mail : [email protected] */#include <iostream>#include <Windows.h>#include <stdio.h>HINSTANCE g_hInst = NULL;HWND g_button = NULL;LRESULT CALLBACK WndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam);/*註冊視窗*/BOOL RegisterWnd(LPSTR pszClassName){ WNDCLASSEX wce = {0}; wce.cbClsExtra = 0; wce.cbSize = sizeof(wce); wce.cbWndExtra = 0; wce.hbrBackground = HBRUSH(COLOR_BTNFACE+1); wce.hCursor = NULL; wce.hIcon = NULL; wce.hIconSm = NULL; wce.hInstance = g_hInst; wce.lpfnWndProc = WndProc; wce.lpszClassName = pszClassName; wce.lpszMenuName = NULL; wce.style = CS_HREDRAW|CS_VREDRAW; ATOM nAtom = RegisterClassEx(&wce); if(0 == nAtom) return FALSE; return TRUE;}/*建立視窗*/HWND CreateWnd(LPSTR pszClassName){ HWND hWnd = CreateWindowEx(0, pszClassName, "MyWnd", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT, NULL,NULL,g_hInst,NULL); return hWnd;} /*顯示視窗*/void DisplayWnd(HWND hWnd){ ShowWindow(hWnd, SW_SHOW); UpdateWindow(hWnd);}void wm_create(HWND hWnd,UINT nMsg, WPARAM wParam,LPARAM lParam){LPCREATESTRUCT pCreateStruct = LPCREATESTRUCT(lParam);/*列印視窗的名字 先彈出這個對話方塊,點擊OK後快顯視窗 *///MessageBox(NULL, pCreateStruct->lpszName,"WM_CREATE",MB_OK);/*建立一個子視窗*/ g_button = CreateWindowEx(0, "BUTTON","BUTTON", WS_CHILD|WS_VISIBLE, 30, 20,100,50, hWnd,NULL,g_hInst,NULL);}void wm_size(HWND hWnd,UINT nMsg, WPARAM wParam,LPARAM lParam){INT nWidth = LOWORD(lParam);INT nHeight = HIWORD(lParam);CHAR szText[256] = {‘\0‘};sprintf(szText,"W:%d;H:%d",nWidth,nHeight);//MessageBox(NULL, szText,"WM_SIZE",MB_OK); if(NULL != g_button) { int nX = (nWidth - 100)/2; int nY = (nHeight - 100)/2; MoveWindow(g_button, nX, nY, 100, 100, TRUE); }}/*執行系統命令函數處理*/BOOL wm_syscommand(HWND hWnd,UINT nMsg, WPARAM wParam,LPARAM lParam){switch(wParam){case SC_CLOSE:if(IDOK == MessageBox(NULL,"是否將檔案存檔","提示",MB_OKCANCEL|MB_ICONWARNING)){return TRUE;} else {return FALSE;}break;default:break;}return FALSE;}/*訊息處理函數*/LRESULT CALLBACK WndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam){ switch(nMsg){/*Win 32 基本訊息 WM_DESTROY: 視窗銷毀時的訊息,可以做退出或善後處理 WM_CREATE: 視窗建立訊息,是在視窗建立後,視窗處理函數收到的第一條訊息 可以在這個訊息內,做初始化或者穿件子視窗 WPARAM wParam - 不使用 LPARAM lParam - CREATESTRUCT指標 WM_SIZE: 當視窗大小發生改變時,會收到這個訊息。 可以在這個訊息中調整視窗的布局 WM_SYSCOMMAND: 系統命令訊息,當點擊系統功能表和按鈕時會收到 可以在這個訊息中,提示使用者儲存資料等 WM_PAINT: 繪圖訊息 鍵盤訊息: 滑鼠訊息 WM_TIME:定時器訊息*/ case WM_DESTROY://視窗銷毀時的訊息/*向視窗發送WM_QUIT 訊息*/ //SendMessage(hWnd, WM_QUIT, 0, 0);/*發送訊息並等候訊息處理結束才返回*/ PostMessage(hWnd, WM_QUIT, 0, 0);/*發送訊息後立即返回,不關心訊息處理的結果*/ //PostQuitMessage(0);return 0;case WM_CREATE:/*建立視窗*/wm_create(hWnd,nMsg,wParam,lParam);break;case WM_SIZE:/*視窗拖動*/wm_size(hWnd,nMsg,wParam,lParam);break;case WM_SYSCOMMAND:/*執行系統命令*/if(!wm_syscommand(hWnd,nMsg,wParam,lParam)){return 0;}break; } return DefWindowProc(hWnd, nMsg, wParam, lParam);}/*訊息迴圈*/void Message(){/* MSG 結構體參數描述typedef struct tagMSG { // msg HWND hwnd; //訊息視窗控制代碼 UINT message;//訊息標示 WPARAM wParam;//訊息的參數 LPARAM lParam;//訊息的參數 DWORD time;//訊息的時間 POINT pt;//訊息產生時,滑鼠的位置 } MSG; */ MSG msg = {0};/* BOOL GetMessage( LPMSG lpMsg,//存放擷取到的訊息資料 由系統填寫關於訊息的參數 HWND hWnd,//擷取訊息的視窗控制代碼,可接受指定視窗訊息 UINT wMsgFilterMin,//訊息過濾的起始訊息 UINT wMsgFilterMax //訊息過濾的終止訊息 ); return : TRUE :成功擷取訊息;FALSE:擷取到WM_QUIT訊息時。 可以使用PostQuitMessage向視窗發送WM_QUIT訊息 GetMessage:擷取訊息,阻塞函數 PeekMessage:擷取訊息,非阻塞函數 */ while(GetMessage(&msg, NULL, 0, 0)) {/*TranslateMessage: 就是將鍵盤的訊息轉換成字元訊息 1、首先檢查是否是鍵盤訊息 2、如果發現是按鍵訊息,將根據按鍵,產生字元訊息 在下一個GetMessage執行時,收到這個訊息 3、如果未發現按鍵訊息,未做任何處理*/ TranslateMessage(&msg);/*DispatchMessage 根據訊息資料內視窗控制代碼,找到這個視窗的視窗處理函數, 調用處理函數,進行訊息處理。 如果MSG中,HWND視窗控制代碼為空白,DispatchMessage不做任何處理。*/ DispatchMessage(&msg); }}int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){ g_hInst = hInstance; RegisterWnd("MyWnd"); HWND hWnd = CreateWnd("MyWnd"); DisplayWnd(hWnd); Message(); return 0;}