关于鼠标的一些细节知识:
通常,我们发消息时,都是对一个特定的窗口,但是对于鼠标消息却不然:只要鼠标跨越窗口或者在某窗口中按下鼠标按键,那么窗口消息处理程序就会收到鼠标消息,而不管该窗口是否活动或者是否拥有输入焦点。鼠标消息一个有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,系统会自动的判断你拖动的是标题栏还是边框而做出相应。