【win32】day04-Win32訊息機制
訊息機制
過程驅動:程式是按照我們預先定義好的順序 執行,每執行一步,下一步都已經按照預定的順序繼續執行,直到程式結束。
事件驅動:程式的執行順序是無序的。某個時間點所執行的程式碼,是由外界通知。由於我們無法決定使用者執行順序,所以程式碼的執行也是無序。
Win32的訊息機制 -事件驅動。
Win32訊息程式
2.1 Win32視窗註冊
2.2 Win32視窗建立
2.3 WIn32訊息迴圈
2.3.1 GetMessage
BOOL GetMessage(
LPMSG lpMsg,//存放獲取到的訊息資料
HWND hWnd,//獲取訊息的視窗控制代碼
UINT wMsgFilterMin,//訊息過濾的起始訊息
UINT wMsgFilterMax //訊息過濾的終止訊息
);
返回值BOOL:成功獲取訊息,返回TRUE,但是當獲取到WM_QUIT訊息時,返回FALSE。
可以使用PostQuitMessage向視窗傳送WM_QUIT訊息。
MSG - 由系統填寫關於訊息的引數hWnd- GetMessage會根據hWnd值,接收由hWnd指定的視窗的訊息。
wMsgFilterMin,wMsgFilterMax -訊息過濾器,要求GetMessage接收指定範圍的訊息。
2.3.2 TranslateMessage
就是將鍵盤訊息轉換成字元訊息。
1 首先檢查是否是鍵盤按鍵訊息
2 如果發現是按鍵訊息,將根據按鍵,產生一個字元訊息,在下一個
3 如果未發現按鍵訊息,不做任何處理。
2.3.3 DispatchMessage
根據訊息資料內視窗控制代碼,找到這個視窗的視窗處理函式, 呼叫處理函式,進行訊息處理。
如果MSG中,HWND視窗控制代碼為空,DispatchMessage不做任何處理。
2.4 Win32基本訊息
2.4.1 WM_DESTROY
視窗銷燬時的訊息,可以做退出或善後處理。
2.4.2 WM_CREATE
視窗建立訊息,是在視窗建立後,視窗處理函式收到第一條訊息。可以在這個訊息內做資料初始化/建立子視窗等。
WPARAM wParam - 不使用
LPARAM lParam - CREATESTRUCT指標
2.4.3 WM_SIZE
當視窗大小發生變化時,會收到這個訊息。可以在這個訊息中調整窗口布局。
wParam - SIZE發生變化時的標識LOWORD(lParam); -客戶區的寬
HIWORD(lParam); - 客戶區的高
2.4.4 WM_SYSCOMMAND
系統命令訊息,當點選系統選單和按鈕時,會收到。
可以在這個訊息中,提示使用者儲存資料等。
wParam - 系統命令型別
LOWORD(lParam) - 螢幕X座標
HIWORD(lParam) - 螢幕Y座標
2.4.5 WM_PAINT 繪圖訊息
2.4.6 鍵盤訊息
2.4.7 滑鼠訊息
2.4.8 WM_TIMER定時器訊息
// WinMsg.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include "stdio.h"
HINSTANCE g_hInst = NULL;
HWND g_hButton = NULL;
LRESULT CALLBACK WndProc( HWND hWnd,
UINT nMsg,
WPARAM wParam,
LPARAM lParam );
BOOL RegisterWnd( LPSTR pszClassName )
{
WNDCLASSEX wce = { 0 };
wce.cbSize = sizeof( wce );
wce.cbClsExtra = 0;
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_VREDRAW|CS_HREDRAW;
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 OnCreate( HWND hWnd, UINT nMsg,
WPARAM wParam, LPARAM lParam )
{
LPCREATESTRUCT pCreateStruct =
LPCREATESTRUCT(lParam);
MessageBox( NULL,
pCreateStruct->lpszName,
"OnCreate", MB_OK );
g_hButton = CreateWindowEx( 0, "BUTTON",
"BUTTON",
WS_CHILD|WS_VISIBLE, 0, 0, 100, 100,
hWnd, NULL, g_hInst, NULL );
}
void OnSize( HWND hWnd, UINT nMsg,
WPARAM wParam, LPARAM lParam)
{
int nWidth = LOWORD( lParam );
int nHeight = HIWORD( lParam );
CHAR szText[260] = {0};
sprintf( szText, "WIDTH:%d,HEIGHT:%d",
nWidth, nHeight );
if( NULL != g_hButton )
{
int nX = ( nWidth - 100 )/2;
int nY = ( nHeight- 100 )/2;
MoveWindow( g_hButton, nX, nY,
100, 100, TRUE );
}
}
BOOL OnSysCommand( HWND hWnd, UINT nMsg,
WPARAM wParam, LPARAM lParam)
{
switch( wParam )
{
case SC_CLOSE:
if( IDOK == MessageBox( NULL, "是否將檔案存檔?",
"提示", MB_OKCANCEL|MB_ICONWARNING ) )
{
return TRUE;
}
return FALSE;
}
return FALSE;
}
LRESULT CALLBACK WndProc( HWND hWnd,
UINT nMsg,
WPARAM wParam,
LPARAM lParam )
{
switch( nMsg )
{
case WM_CREATE:
OnCreate( hWnd, nMsg, wParam, lParam );
break;
case WM_SIZE:
OnSize( hWnd, nMsg, wParam, lParam );
break;
case WM_SYSCOMMAND:
if( FALSE == OnSysCommand( hWnd, nMsg,
wParam, lParam ) )
{
return 0;
}
break;
case WM_DESTROY://視窗銷燬時的訊息
PostQuitMessage( 0 ); //WM_QUIT
return 0;
}
//使用系統函式處理我們未處理的訊息型別
return DefWindowProc( hWnd, nMsg,
wParam, lParam );
}
void Message( )
{
MSG msg = { 0 };
while( GetMessage( &msg, NULL, 0, 0 ) )
{
TranslateMessage( &msg );
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;
}
2.5 訊息結構
MSG - 訊息結構
typedef struct tagMSG { // msg
HWND hwnd; //訊息的視窗控制代碼
UINT message;//訊息標示
WPARAM wParam; //訊息的引數,32位
LPARAM lParam; //訊息的引數,32位
DWORD time;//訊息產生的時間
POINT pt; //訊息產生時,滑鼠的位置
} MSG;
2.6 訊息的獲取和傳送
2.6.1 獲取GetMessage/PeekMessage
GetMessage 獲取訊息,阻塞函式
PeekMessage 獲取訊息,非阻塞函式
2.6.2 傳送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.1 訊息佇列 -用於儲存訊息的記憶體空間,
訊息在佇列中是先入先出.
4.2 訊息佇列的分類
4.2.1 系統訊息佇列 -由系統維護的訊息佇列.
4.2.2 應用程式訊息佇列(執行緒訊息對列) -
屬於每個執行緒的各自擁有的訊息佇列.
訊息和訊息佇列
5.1 根據訊息和訊息佇列關係,將訊息分成兩種:
佇列訊息 - 可以存放在訊息佇列中的訊息.
非佇列訊息 - 傳送時不進入訊息佇列.
5.2 佇列訊息
首先存放到訊息隊列當中,然後由GetMessage /PeekMessage取出,然後進行處理.
例如: 滑鼠訊息/鍵盤訊息/WM_PAINT/WM_QUIT/ WM_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產生非佇列訊息,可以確定訊息是否成功.
WM_PAINT訊息
1 WM_PAINT的產生
由於視窗的互相覆蓋等,產生需要繪製的區域,那麼會產生WM_PAINT訊息.
一般情況下,不直接傳送WM_PAINT訊息,通過API宣告需要繪製區域,來產生WM_PAINT訊息.
例如,可以使用InvalidateRect宣告一個需要重新繪製的區域.
2 WM_PAINT的注意點
2.1 如果一個訊息佇列中,有多個WM_PAINT訊息,只有最後一個WM_PAINT訊息會被處理.
2.2 WM_PAINT訊息處理中,要清空需要被繪製的區域. BeginPaint
3 WM_PAINT的使用
3.1 WM_PAINT開始時,必須呼叫BeginPaint
3.2 繪製圖形
3.3 WM_PAINT處理後,必須呼叫EndPaint
// WinPaint.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
HINSTANCE g_hInst = NULL;
void OnPaint( HWND hWnd, UINT nMsg,
WPARAM wParam, LPARAM lParam )
{ //WM_PAINT開始時,必須呼叫
PAINTSTRUCT ps = {0};
HDC hDC = BeginPaint( hWnd, &ps );
CHAR szText[] = "Hello WM_PAINT";
TextOut( hDC, 100, 100, szText, strlen(szText) );
Rectangle( hDC, 200, 200, 300, 300 );
//WM_PAINT處理後,必須呼叫
EndPaint( hWnd, &ps );
}
LRESULT CALLBACK WndProc( HWND hWnd,
UINT nMsg,
WPARAM wParam,
LPARAM lParam )
{
switch( nMsg )
{
case WM_PAINT:
OnPaint( hWnd, nMsg, wParam, lParam );
break;
case WM_DESTROY:
PostQuitMessage( 0 );
return 0;
}
return DefWindowProc( hWnd, nMsg,
wParam, lParam );
}
BOOL RegisterWnd( LPSTR pszClassName )
{
WNDCLASSEX wce = { 0 };
wce.cbSize = sizeof( wce );
wce.cbClsExtra = 0;
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 Message( )
{
MSG msg = { 0 };
while( GetMessage(&msg, NULL, 0, 0 ) )
{
TranslateMessage( &msg );
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;
}
鍵盤訊息
1 鍵盤訊息
按鍵訊息
WM_KEYDOWN 當鍵被按下時產生
WM_KEYUP 當鍵被釋放時產生
WM_SYSKEYDOWN 當系統鍵被按下時產生 ALT/F10
WM_SYSKEYUP 當系統鍵釋放時產生
字元訊息
WM_CHAR 當有字元鍵被按下時產生
TranslateMessage會將WM_KEYDOWN訊息中,可以顯示的按鍵,轉換成WM_CHAR的訊息。
2 訊息引數
WPARAM - 虛擬鍵碼
LPARAM - 相關的按鍵資訊.
3 訊息的使用
3.1 當有按鍵訊息時,首先進入系統訊息佇列,然後別程式的訊息迴圈獲取.
3.2 訊息的處理
4 鍵盤訊息的順序
對於可顯示字元: WM_KEYDOWN,WM_CHAR,WM_KEYUP
對於不可顯示字元: WM_KEYDOWN,WM_KEYUP
對於系統鍵:WM_SYSKEYDOWN,WM_SYSKEYUP
如果按鍵一直不釋放,會重複產生WM_KEYDOWN(WM_CHAR),WM_KEYUP只有一次。
// WinKeyboard.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include "stdio.h"
HINSTANCE g_hInst = NULL;
HANDLE g_hStdOut = NULL;
LRESULT CALLBACK WndProc( HWND hWnd,
UINT nMsg,
WPARAM wParam,
LPARAM lParam )
{
switch( nMsg )
{
case WM_KEYDOWN:
{
CHAR szText[]= "WM_KEYDOWN\n";
switch( wParam )
{
case VK_LEFT:
WriteConsole( g_hStdOut, szText,
strlen(szText), NULL, NULL );
break;
case VK_RIGHT:
WriteConsole( g_hStdOut, szText,
strlen(szText), NULL, NULL );
break;
case VK_UP:
WriteConsole( g_hStdOut, szText,
strlen(szText), NULL, NULL );
break;
case VK_DOWN:
WriteConsole( g_hStdOut, szText,
strlen(szText), NULL, NULL );
break;
}
}
break;
case WM_KEYUP:
{
CHAR szText[]= "WM_KEYUP\n";
WriteConsole( g_hStdOut, szText,
strlen(szText), NULL, NULL );
}
break;
case WM_SYSKEYDOWN:
{
CHAR szText[]= "WM_SYSKEYDOWN\n";
WriteConsole( g_hStdOut, szText,
strlen(szText), NULL, NULL );
}
break;
case WM_SYSKEYUP:
{
CHAR szText[]= "WM_SYSKEYUP\n";
WriteConsole( g_hStdOut, szText,
strlen(szText), NULL, NULL );
}
break;
case WM_CHAR:
{
CHAR szText[260] = {0};
sprintf( szText, "WM_CHAR: %c\n", wParam);
WriteConsole( g_hStdOut, szText,
strlen(szText), NULL, NULL );
}
break;
case WM_DESTROY:
PostQuitMessage( 0 );
return 0;
}
return DefWindowProc( hWnd, nMsg,
wParam, lParam );
}
BOOL RegisterWnd( LPSTR pszClassName )
{
WNDCLASSEX wce = { 0 };
wce.cbSize = sizeof( wce );
wce.cbClsExtra = 0;
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 Message( )
{
MSG msg = { 0 };
while( GetMessage(&msg, NULL, 0, 0 ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}
void NewConsole( )
{
AllocConsole( );
g_hStdOut =
GetStdHandle( STD_OUTPUT_HANDLE );
CHAR szText[] = "Debug Message......:\n";
WriteConsole( g_hStdOut, szText,
strlen(szText), NULL, NULL );
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
NewConsole( );
g_hInst = hInstance;
RegisterWnd( "MYWND" );
HWND hWnd = CreateWnd( "MYWND" );
DisplayWnd( hWnd );
Message( );
return 0;
}