1. 程式人生 > >windows程式設計(14):滑鼠訊息詳解

windows程式設計(14):滑鼠訊息詳解

關於滑鼠的一些細節知識:

通常,我們發訊息時,都是對一個特定的視窗,但是對於滑鼠訊息卻不然:只要滑鼠跨越視窗或者在某視窗中按下滑鼠按鍵,那麼視窗訊息處理程式就會收到滑鼠訊息,而不管該視窗是否活動或者是否擁有輸入焦點。滑鼠訊息一個有21種:10個顯示區域訊息,11個非顯示區域訊息

顯示區域滑鼠訊息

當滑鼠移過視窗的顯示區域時,視窗訊息處理程式收到WM_MOUSEMOVE訊息。

當在視窗的顯示區域中按下或者釋放一個滑鼠按鍵時,視窗訊息處理程式會接收到下面這些訊息:

按下

釋放

按下(雙鍵)

WM_LBUTTONDOWN

WM_LBUTTONUP

WM_LBUTTONDBLCLK

WM_MBUTTONDOWN

WM_MBUTTONUP

WM_MBUTTONDBLCLK

WM_RBUTTONDOWN

WM_RBUTTONUP

WM_RBUTTONDBLCLK

只有對三鍵滑鼠,視窗訊息處理程式才會收到MBUTTON訊息。

對於這些訊息,其lParam值均含有滑鼠的位置:低字位元組為x座標,高位元組為y座標,這兩個座標是相對於視窗顯示區域左上角的位置。您可以用LOWORD和HIWORD巨集來提取這些值:

x = LOWORD (lParam) ;       
y = HIWORD (lParam)  ;

wParam的值指示滑鼠按鍵以及Shift和Ctrl鍵的狀態:

MK_LBUTTON

按下左鍵

MK_MBUTTON

按下中鍵

MK_RBUTTON

按下右鍵

MK_SHIFT

按下Shift鍵

MK_CONTROL

按下Ctrl鍵

舉個例子,如果收到了WM_LBUTTONDOWN且wparam & MK_SHIFT為真,則說明左鍵按下時也按下了Shift鍵。

這裡要強調一下:

1.WM_MOUSEMOVE訊息:當您把滑鼠移過視窗的顯示區域時,Windows並不為滑鼠的每個可能的圖素位置都產生一個WM_MOUSEMOVE訊息。您的程式接收到WM_MOUSEMOVE訊息的次數,依賴於滑鼠硬體,以及您的視窗訊息處理程式在處理滑鼠移動訊息時的速度。換句話說,Windows不能用未處理的WM_MOUSEMOVE訊息來填入訊息佇列。

2.對於WM_LBUTTONDOWN和WM_LBUTTONUP訊息可能只收到一個!比如在一個視窗中按下滑鼠按鍵,然後移動到使用者視窗釋放它,就會出現這種情況。類似的情況,當滑鼠按鍵在另一個視窗中被釋放時,視窗訊息處理程式只能接收到WM_LBUTTONDOWN訊息,而沒有相應的WM_LBUTTONUP訊息。

3.關於雙擊滑鼠:雙擊的間隔在控制面板裡可以設定。但是是否需要相應“雙擊”則是我們自己寫的:在註冊視窗時,必須在視窗風格中包含CS_DBLCLKS識別符號。這樣當你雙擊時,系統會收到:

WM_LBUTTONDOWN

WM_LBUTTONUP

WM_LBUTTONDBLCLK

WM_LBUTTONUP

否則,只會收到:

WM_LBUTTONDOWN

WM_LBUTTONUP

WM_LBUTTONDOWN

WM_LBUTTONUP


看一個例子程式:

/*--------------------------------------------------
   CONNECT.C -- Connect-the-Dots Mouse Demo Program
			 (c) Charles Petzold, 1998
  --------------------------------------------------*/

#include <windows.h>

#define MAXPOINTS 1000

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
				PSTR szCmdLine, int iCmdShow)
{
	 
	static TCHAR	szAppName[] = TEXT ("Connect") ;
	HWND			hwnd ;
	MSG				msg ;
	WNDCLASS		wndclass ;
	
	wndclass.style			= CS_HREDRAW | CS_VREDRAW ;
	wndclass.lpfnWndProc	= WndProc ;
	wndclass.cbClsExtra		= 0 ;
	wndclass.cbWndExtra		= 0 ;
	wndclass.hInstance		= hInstance ;
	wndclass.hIcon			= LoadIcon (NULL, IDI_APPLICATION) ;
	wndclass.hCursor		= LoadCursor (NULL, IDC_ARROW) ;
	wndclass.hbrBackground	= (HBRUSH) GetStockObject (WHITE_BRUSH) ;
	wndclass.lpszMenuName	= NULL ;
	wndclass.lpszClassName	= szAppName ;
	
	if (!RegisterClass (&wndclass))
	{
		MessageBox (NULL, TEXT ("Program requires Windows NT!"), 
				  szAppName, MB_ICONERROR) ;
		return 0 ;
	}
	
	hwnd = CreateWindow (szAppName, TEXT ("Connect-the-Points Mouse Demo"),
					 WS_OVERLAPPEDWINDOW,
					 CW_USEDEFAULT, CW_USEDEFAULT,
					 CW_USEDEFAULT, CW_USEDEFAULT,
					 NULL, NULL, hInstance, NULL) ;
	
	ShowWindow (hwnd, iCmdShow) ;
	UpdateWindow (hwnd) ;
	
	while (GetMessage (&msg, NULL, 0, 0))
	{
		TranslateMessage (&msg) ;
		DispatchMessage (&msg) ;
	}
	return msg.wParam ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static POINT	pt[MAXPOINTS] ;
	static int		iCount ;
	HDC				hdc ;
	int				i, j ;
	PAINTSTRUCT		ps ;

	switch (message)
	{
	case WM_LBUTTONDOWN:
		iCount = 0 ;
		InvalidateRect (hwnd, NULL, TRUE) ;
		return 0 ;
		
	case WM_MOUSEMOVE:
		//是否按下了左鍵且收到的點數不到1000
		if (wParam & MK_LBUTTON && iCount < 1000)
		{
			//記錄滑鼠位置並在最後iCount+1
			pt[iCount  ].x = LOWORD (lParam) ;
			pt[iCount++].y = HIWORD (lParam) ;
			
			hdc = GetDC (hwnd) ;
			//在指定的位置設定指定顏色的點
			SetPixel (hdc, LOWORD (lParam), HIWORD (lParam), 0) ;
			ReleaseDC (hwnd, hdc) ;
		}
		return 0 ;
		
	case WM_LBUTTONUP:
		InvalidateRect (hwnd, NULL, FALSE) ;
		return 0 ;
		
	case WM_PAINT:
		hdc = BeginPaint (hwnd, &ps) ;
		//畫圖時游標為等待狀態
		SetCursor (LoadCursor (NULL, IDC_WAIT)) ;
		ShowCursor (TRUE) ;
		
		for (i = 0 ; i < iCount - 1 ; i++)
			for (j = i + 1 ; j < iCount ; j++)
			{
				MoveToEx (hdc, pt[i].x, pt[i].y, NULL) ;
				LineTo   (hdc, pt[j].x, pt[j].y) ;
			}
			
		ShowCursor (FALSE) ;
		SetCursor (LoadCursor (NULL, IDC_ARROW)) ;
			
		EndPaint (hwnd, &ps) ;
		return 0 ;
			
	case WM_DESTROY:
		PostQuitMessage (0) ;
		return 0 ;
	}
	return DefWindowProc (hwnd, message, wParam, lParam) ;
}

程式比較簡單,就不多說了。

那什麼是非顯示區域的滑鼠訊息呢?如果滑鼠在視窗的顯示區域之外但還在視窗內,Windows就給視窗訊息處理程式傳送一條“非顯示區域”滑鼠訊息。視窗非顯示區域包括標題列、選單和視窗滾動條。

這些訊息通常我們是不用管的,將這些訊息傳給DefWindowProc,從而使Windows執行系統功能。

非顯示區域滑鼠訊息幾乎完全與顯示區域滑鼠訊息相對應。訊息中含有字母“NC”以表示是非顯示區域訊息。如果滑鼠在視窗的非顯示區域中移動,那麼視窗訊息處理程式會接收到WM_NCMOUSEMOVE訊息。滑鼠按鍵產生如表所示的訊息:

按下

釋放

按下(雙擊)

WM_NCLBUTTONDOWN

WM_NCLBUTTONUP

WM_NCLBUTTONDBLCLK

WM_NCMBUTTONDOWN

WM_NCMBUTTONUP

WM_NCMBUTTONDBLCLK

WM_NCRBUTTONDOWN

WM_NCRBUTTONUP

WM_NCRBUTTONDBLCLK

但是這些訊息引數的意義卻不同:

wParam引數指明移動或者按滑鼠按鍵的非顯示區域。

lParam引數的低位word為x座標,高位word為y座標,但是,它們是螢幕座標。

最後一個訊息是WM_NCHITTEST,它代表“非顯示區域命中測試”,Windows應用程式通常把這個訊息傳送給DefWindowProc,系統會自動的判斷你拖動的是標題欄還是邊框而做出相應。