3.鼠标消息
(1)基本鼠标消息
WM_LBUTTONDOWN - 鼠标左键按下消息
WM_LBUTTONUP - 鼠标左键抬起消息
WM_RBUTTONDOWN - 鼠标右键按下消息
WM_RBUTTONUP - 鼠标右键抬起消息
WM_MOUSEMOVE - 鼠标移动消息
消息参数:wPARAM - 其他按键状态,例如Ctrl/Shift等
lPARAM - 鼠标的位置,窗口客户区坐标系
LOWORD - X坐标位置,HIWORD - Y坐标位置
使用:一般情况下鼠标按下/抬起成对出现,鼠标移动过程中,会根据移动速度产生一系列的WM_MOUSEMOVE消息。
(2)双击消息
WM_LBUTTONDBLCLK - 鼠标左键双击消息
WM_RBUTTONDBLCLK - 鼠标右键双击消息
消息参数:wPARAM - 其他按键的状态,例如Ctrl/Shift等
lPARAM - 鼠标的位置,窗口客户区坐标系。
LOWORD - X坐标位置,HIWORD - Y坐标位置
使用:使用时需要在注册窗口类的时候添加CS_DBCLKS风格。
消息产生顺序:(以WM_LBUTTONDBCLK为例)
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_LBUTTONDBLCLK
WM_LBUTTONUP
(3)滚轮消息
WM_MOUSEWHEEL - 鼠标滚轮消息
消息参数:wPARAM:
LOWORD - 其他按键的状态
HIWORD - 滚轮的偏移量,是120的倍数,通过正负值表示滚动方向
lPARAM:鼠标的位置,屏幕坐标系
LOWORD - X坐标
HIWORD - Y坐标
使用:通过偏移量,获取滚动的方向和倍数
下面是相关代码:
#include "stdafx.h" #include <stdio.h> HINSTANCE g_hInstance = 0; //接收当前程序实例句柄 HANDLE g_hOutput = 0; void OnLButtonDown(HWND hWnd, WPARAM wParam, LPARAM lParam) { char szText[256] = { 0 }; sprintf_s(szText, "WM_LBUTTONDOWN:按键状态:%08x, x=%d, y=%d ", wParam, LOWORD(lParam), HIWORD(lParam)); //WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL); } void OnLButtonUp(HWND hWnd, WPARAM wParam, LPARAM lParam) { char szText[256] = { 0 }; sprintf_s(szText, "WM_LBUTTONUP:按键状态:%08x, x=%d, y=%d ", wParam, LOWORD(lParam), HIWORD(lParam)); //WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL); } void OnMouseMove(HWND hWnd, LPARAM lParam) { short nX = LOWORD(lParam); short nY = HIWORD(lParam); char szText[256] = { 0 }; sprintf_s(szText, "鼠标位置:x=%d, y=%d ", nX, nY); //WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL); } void OnLButtonDblClk(HWND hWnd, LPARAM lParam) { short nX = LOWORD(lParam); short nY = HIWORD(lParam); char szText[256] = { 0 }; sprintf_s(szText, "WM_LBUTTONDBLCLK:x=%d, y=%d ", nX, nY); WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL); } void OnMouseWheel(HWND hWnd, WPARAM wParam) { short nDelta = HIWORD(wParam); char szText[265] = { 0 }; sprintf_s(szText, "偏移量:%d ", nDelta); //WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL); } //窗口处理函数 LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_MOUSEWHEEL: OnMouseWheel(hWnd, wParam); break; case WM_LBUTTONDBLCLK: OnLButtonDblClk(hWnd, lParam); break; case WM_MOUSEMOVE: OnMouseMove(hWnd, lParam); break; case WM_LBUTTONDOWN: OnLButtonDown(hWnd, wParam, lParam); break; case WM_LBUTTONUP: OnLButtonUp(hWnd, wParam, lParam); break; case WM_DESTROY: PostQuitMessage(0); break; } return DefWindowProc(hWnd, msg, wParam, lParam); } //注册窗口类 BOOL Register(LPSTR lpClassName, WNDPROC wndProc) { WNDCLASSEX wce = { 0 }; wce.cbSize = sizeof(wce); wce.cbClsExtra = 200; wce.cbClsExtra = 200; wce.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wce.hCursor = NULL; wce.hIcon = NULL; wce.hIconSm = NULL; wce.hInstance = g_hInstance; wce.lpfnWndProc = wndProc; wce.lpszClassName = lpClassName; wce.lpszMenuName = NULL; wce.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; ATOM nAtom = RegisterClassEx(&wce); if (0 == nAtom) { return FALSE; } return TRUE; } //创建主窗口 HWND CreateMainWindow(LPSTR lpClassName, LPSTR lpWndName) { HWND hWnd = CreateWindowEx(0, lpClassName, lpWndName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, g_hInstance, NULL); return hWnd; }//显示窗口 void Display(HWND hWnd) { ShowWindow(hWnd, SW_SHOW); UpdateWindow(hWnd); } //消息循环 void Message() { MSG nMsg = { 0 }; while (GetMessage(&nMsg, NULL, 0, 0)) { TranslateMessage(&nMsg); DispatchMessage(&nMsg); } } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { AllocConsole(); g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE); g_hInstance = hInstance; Register("Main", WndProc); HWND hWnd = CreateMainWindow("Main", "window"); Display(hWnd); Message(); return 0; }
(4)定时器消息
可以再程序中设置定时器,当到达时间间隔时,定时器会向程序触发一个WM_TIMER消息(准确说是GetMessage函数发送消息)。
定时器的精度是毫秒,但是准确度很低。例如设置时间间隔为1000ms,但是会在非1000ms内到达。
消息参数:wPARAM - 定时器ID
lPARAM - 定时器处理函数的指针
使用:
a.创建定时器
UINT_PTR SetTimer( HWND hWnd, //定时器窗口句柄,指定处理消息的窗口
UINT_PTR nIDEvent, //定时器ID
UINT uElapse, //时间间隔
TIMERPROC lpTimerFunc); //定时器处理函数指针
创建成功返回非0。
lpTimerFunc非空,使用定时器处理函数处理定时器(WM_TIMER)消息。
lpTimerFunc为NULL,使用指定窗口的窗口处理函数处理定时器(WM_TIMER)消息。
定时器处理函数:
VOID CALLBACK TimerProc( HWND hWnd, //窗口句柄
UINT uMsg, //消息ID,WM_TIMER
UINT_PTR idEvent, //定时器ID
DWORD dwTime); //当前系统时间
b.处理WM_TIMER
c.关闭定时器
BOOL KillTimer( HWND hWnd, //定时器窗口句柄
UINT_PTR uIDEvent); //定时器ID
附:GetClientRect - 获取窗口客户区大小
介绍会用到的函数
画圆:
BOOL Ellipse( HDC hdc, //dc句柄(BeginPaint返回)
int nLeftRect, //左上角x坐标
int nTopRect, //左上角y坐标
int nRightRect, //右下角x坐标
int nBottomRect); //右下角y坐标
获取窗口的边界信息:
BOOL GetClientRect( HWND hWnd, //窗口句柄
LPRECT lpRect); //返回窗口的边界信息
下面是用定时器让一个小圆移动的代码:
#include "stdafx.h" #include "stdio.h" HINSTANCE g_hInstance = 0; //接收当前程序实例句柄 HANDLE g_hOutput = 0; //圆的位置 int g_xPos = 100; int g_yPos = 100; //圆运动的方向 BOOL Left_Right = TRUE; BOOL Right_Left = FALSE; BOOL Up_Down = TRUE; BOOL Down_Up = FALSE; void OnTimer(HWND hWNd, WPARAM wParam) { char szText[256] = { 0 }; sprintf_s(szText, "定时器%d ", wParam); WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL); RECT rc = { 0 }; GetClientRect(hWNd, &rc); if (g_xPos <= rc.left) { Left_Right = TRUE; Right_Left = FALSE; } else if (g_xPos >= rc.right-50) { Right_Left = TRUE; Left_Right = FALSE; } if (Left_Right) { g_xPos++; } else if (Right_Left) { g_xPos--; } if (g_yPos >= rc.bottom - 50) { Down_Up = TRUE; Up_Down = FALSE; } else if (g_yPos <= rc.top) { Up_Down = TRUE; Down_Up = FALSE; } if (Up_Down) { g_yPos++; } else if (Down_Up) { g_yPos--; } InvalidateRect(hWNd, NULL, FALSE); } void CALLBACK TimerProc(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) { char szText[256] = { 0 }; sprintf_s(szText, "定时器处理函数处理:%d ", idEvent); WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL); } void OnPaint(HWND hWnd) { PAINTSTRUCT ps = { 0 }; HDC hdc = BeginPaint(hWnd, &ps); Ellipse(hdc, g_xPos, g_yPos, g_xPos+50, g_yPos+50); EndPaint(hWnd, &ps); } //窗口处理函数 LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_PAINT: OnPaint(hWnd); break; case WM_TIMER: OnTimer(hWnd, wParam); break; case WM_CREATE: { SetTimer(hWnd, 1, 10, NULL); //SetTimer(hWnd, 2, 2000, TimerProc); } break; case WM_DESTROY: PostQuitMessage(0); break; } return DefWindowProc(hWnd, msg, wParam, lParam); } //注册窗口类 BOOL Register(LPSTR lpClassName, WNDPROC wndProc) { WNDCLASSEX wce = { 0 }; wce.cbSize = sizeof(wce); wce.cbClsExtra = 200; wce.cbClsExtra = 200; wce.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wce.hCursor = NULL; wce.hIcon = NULL; wce.hIconSm = NULL; wce.hInstance = g_hInstance; wce.lpfnWndProc = wndProc; wce.lpszClassName = lpClassName; wce.lpszMenuName = NULL; wce.style = CS_HREDRAW | CS_VREDRAW; ATOM nAtom = RegisterClassEx(&wce); if (0 == nAtom) { return FALSE; } return TRUE; } //创建主窗口 HWND CreateMainWindow(LPSTR lpClassName, LPSTR lpWndName) { HWND hWnd = CreateWindowEx(0, lpClassName, lpWndName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, g_hInstance, NULL); return hWnd; }//显示窗口 void Display(HWND hWnd) { ShowWindow(hWnd, SW_SHOW); UpdateWindow(hWnd); } //消息循环 void Message() { MSG nMsg = { 0 }; while (GetMessage(&nMsg, NULL, 0, 0)) { TranslateMessage(&nMsg); DispatchMessage(&nMsg); } } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { AllocConsole(); g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE); g_hInstance = hInstance; Register("Main", WndProc); HWND hWnd = CreateMainWindow("Main", "window"); Display(hWnd); Message(); return 0; }
运行结果: