1. 程式人生 > >【win32】day04-Win32訊息機制

【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指定的視窗的訊息。

wMsgFilterMinwMsgFilterMax -訊息過濾器,要求GetMessage接收指定範圍的訊息。

2.3.2 TranslateMessage

就是將鍵盤訊息轉換成字元訊息。

1 首先檢查是否是鍵盤按鍵訊息    

2 如果發現是按鍵訊息,將根據按鍵,產生一個字元訊息,在下一個

GetMessage執行時,會收到這個訊息。

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_USERID開始,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 GetMesssagePeekMessage

  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 PostMessageSendMessage

  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;
}