相关函数预览:
int WINAPI GetSystemMetrics( _In_ int nIndex);
BOOL WINAPI ScreenToClient( _In_ HWND hWnd, _Inout_ LPPOINT lpPoint);
BOOL WINAPI ClientToScreen( _In_ HWND hWnd, _Inout_ LPPOINT lpPoint);
BOOL WINAPI GetCursorPos( _Out_ LPPOINT lpPoint);
BOOL WINAPI SetCursorPos( _In_ int X, _In_ int Y);
LONG WINAPI SetWindowLongW( _In_ HWND hWnd, _In_ int nIndex, _In_ LONG dwNewLong);
LON WINAPI GetWindowLongW( _In_ HWND hWnd, _In_ int nIndex);
HWND WINAPI SetFocus( _In_opt_ HWND hWnd);//返回值为当前具有键盘焦点的窗口句柄
HWND WINAPI GetFocus( VOID);
HWND WINAPI GetParent( _In_ HWND hWnd);
HWND WINAPI GetDlgItem( _In_opt_ HWND hDlg, _In_ int nIDDlgItem);
HWND WINAPI SetCapture( _In_ HWND hWnd);//捕获鼠标
BOOL WINAPI ReleaseCapture( VOID);//释放鼠标
GetWindowLong与SetWindowLong函数中Index在参考程序中的值:
GWL_ID(-12) | 获得窗口标识 |
GWL_USERDATA(-21) | 获得或修改和窗口相关联的32位的值(每一个窗口都有一个有意留给创建窗口的应用程序是用的32位 的值) |
更多取值可参考MSDN。
当计算机上安装了鼠标时,fMouse = GetSystemMetrics(SM_MOUSEPRESENT);的返回值为true,否则为0。
鼠标消息:
WIndows定义了22种鼠标消息,其中和客户区相关的有10种,11种非客户区鼠标消息,1个鼠标滚轮消息。
客户区鼠标消息:
WM_MOUSEMOVE:鼠标移动消息。在消息队列种只会存在最多一个鼠标移动消息,即操作系统不会为鼠标经过的每一个像素产生一个WM_MOUSEMOVE消息,而是在当前消息处理结束后才会产生下一个消息。
左键消息:
WM_LUBTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK三种消息,需要注意的是,在实际测试中,若窗口过程处理了LUBTTONDOWM消息后,鼠标没有移动,则不会收到LBUTTONUP消息。
中键与右键消息仅需要把LBUTTON中的L对应替换为M和R即可。
需要注意的是,如果窗口过程需要处理鼠标双击的消息,必须在窗口类的style字段包含标识符CS_DBLCLKS。
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MOUSE1));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_MOUSE1);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
当我们的窗口可以接收双击消息时,窗口过程收到的消息顺序依次为:WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK, WM_LBUTTONUP。对于客户区鼠标消息,参数lParam包含了鼠标的位置消息,其中
x = LOWORD(lParam); y = HIWORD(lParam);
参数wParam表示鼠标按钮,shift键与ctrl键的状态可与下列flag进行与运算来判断相关键的状态。
MK_LBUTTON | 按下左键 |
MK_MBUTTON | 按下中键 |
MK_RBUTTON | 按下右键 |
MK_SHIFT | 按下shift键 |
MK_CONTROL | 按下ctrl键 |
例如,在WM_LBUTTONDOWN消息中,wParam & MK_CONTROL == true,则说明按下左键的同时也按下了ctrl键。
非客户区鼠标消息:
非客户区鼠标消息中有10个与客户区鼠标消息一一对应,在消息标识符中包含了字母NC(nonclient)如:WM_NCMOUSEMOVE, WM_NCMBUTTONDOWN等。这些消息的lParam参数为鼠标的屏幕坐标,通常情况下我们不需要处理这些消息。
另一个非客户区消息为鼠标击中测试消息WM_NCHITTEST。该消息的lParam参数包含了鼠标的屏幕坐标,wParam参数没有用到。Windows通常把这个消息交给DefWindowProc来处理,以产生前面所述的20种鼠标消息。
鼠标滚轮消息 WM_MOUSEWHEEL:lParam参数包含了鼠标的屏幕坐标信息,wParam参数的低位字包含了鼠标按钮,shift和ctrl键的状态,高位字为增量(delta),取值通常为120或-120。可以使用ScreenToClient函数与ClientToScreen函数进行屏幕坐标与客户区坐标的转换。
参考程序是对Windows程序设计种的击中测试做了少量修改。其功能为:窗口产生25个子窗口,当键盘输入焦点在某一个子窗口上时,该窗口显示一个虚线画成的矩形,当某窗口被鼠标击中时,该窗口画一个X,第二次被击中时,X消失。参考程序分为三部分,第一部分为注册窗口类,主函数等,第二部分为主窗口的窗口过程,第三部分为子窗口的窗口过程。
第一部分:
//start
#define DIVISIONS 5
LRESULT CALLBACK ChildWndProc(HWND, UINT, WPARAM, LPARAM);
TCHAR szChildClass[] = TEXT("Checker3_Child");
int idFocus = 0;
//End
// 全局变量:
HINSTANCE hInst; // 当前实例
WCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名
// 此代码模块中包含的函数的前向声明:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: 在此放置代码。
// 初始化全局字符串
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_MOUSE1, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
///
/
// 执行应用程序初始化:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MOUSE1));
MSG msg;
// 主消息循环:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
//
// 函数: MyRegisterClass()
//
// 目的: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MOUSE1));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_MOUSE1);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
//
if (!RegisterClassExW(&wcex))return 0;
wcex.lpfnWndProc = ChildWndProc;
wcex.cbWndExtra = sizeof(long);
wcex.hIcon = nullptr;
wcex.lpszClassName = szChildClass;
/
return RegisterClassExW(&wcex);
}
//
// 函数: InitInstance(HINSTANCE, int)
//
// 目的: 保存实例句柄并创建主窗口
//
// 注释:
//
// 在此函数中,我们在全局变量中保存实例句柄并
// 创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // 将实例句柄存储在全局变量中
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
第二部分:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HWND hwndChild[DIVISIONS][DIVISIONS];
int cxBlock, cyBlock, x, y;
switch (message)
{
case WM_CREATE:
for (x = 0; x < DIVISIONS; ++x)
for (y = 0; y < DIVISIONS; ++y)
hwndChild[x][y] = CreateWindowW(szChildClass, nullptr, WS_CHILDWINDOW | WS_VISIBLE, 0, 0, 0, 0, hWnd, (HMENU)(y << 8 | x), (HINSTANCE)GetWindowLong(hWnd, -6), nullptr);
return 0;
case WM_SIZE:
cxBlock = LOWORD(lParam) / DIVISIONS;
cyBlock = HIWORD(lParam) / DIVISIONS;
for (x = 0; x < DIVISIONS; ++x)
for (y = 0; y < DIVISIONS; ++y)
MoveWindow(hwndChild[x][y], x * cxBlock, y * cyBlock, cxBlock, cyBlock, true);
return 0;
case WM_LBUTTONDOWN:
MessageBeep(0);
return 0;
case WM_SETFOCUS:
SetFocus(GetDlgItem(hWnd, idFocus));
return 0;
case WM_KEYDOWN:
x = idFocus & 0xff;
y = idFocus >> 8;
switch (wParam)
{
case VK_UP: --y; break;
case VK_DOWN: ++y; break;
case VK_LEFT: --x; break;
case VK_RIGHT:++x; break;
case VK_HOME:x = y = 0; break;
case VK_END: x = y = DIVISIONS - 1; break;
default:return 0;
}
x = (x + DIVISIONS) % DIVISIONS;
y = (y + DIVISIONS) % DIVISIONS;
idFocus = y << 8 | x;
SetFocus(GetDlgItem(hWnd, idFocus));
return 0;
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: 在此处添加使用 hdc 的任何绘图代码...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
第三部分:
LRESULT CALLBACK ChildWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
switch (message)
{
case WM_CREATE:
SetWindowLong(hWnd, -21, 0);
return 0;
case WM_LBUTTONDOWN:
SetWindowLong(hWnd, -21, 1 ^ GetWindowLong(hWnd, -21));
SetFocus(hWnd);
// MessageBeep(MB_ICONEXCLAMATION);
InvalidateRect(hWnd, nullptr, false);
return 0;
case WM_SETFOCUS:
idFocus = GetWindowLong(hWnd, GWL_ID);
// return 0;
case WM_KILLFOCUS:
InvalidateRect(hWnd, nullptr, true);
return 0;
case WM_KEYDOWN:
if (wParam != VK_RETURN && wParam != VK_SPACE)
{
SendMessage(GetParent(hWnd), message, wParam, lParam);
return 0;
}
else
{
SendMessage(hWnd, WM_LBUTTONDOWN, 0, 0);
return 0;
}
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
GetClientRect(hWnd, &rect);
Rectangle(hdc, 0, 0, rect.right, rect.bottom);
if (GetWindowLong(hWnd, -21))
{
MoveToEx(hdc, 0, 0, nullptr);
LineTo(hdc, rect.right, rect.bottom);
MoveToEx(hdc, 0, rect.bottom, nullptr);
LineTo(hdc, rect.right, 0);
}
if (hWnd == GetFocus())
{
rect.left += rect.right / 10;
rect.right -= rect.left;
rect.top += rect.bottom / 10;
rect.bottom -= rect.top;
SelectObject(hdc, GetStockObject(NULL_BRUSH));
SelectObject(hdc, CreatePen(PS_DASH, 0, 0));
Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom);
DeleteObject(SelectObject(hdc, GetStockObject(BLACK_PEN)));
}
EndPaint(hWnd, &ps);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
参考文献:《Windows程序设计 第5版 珍藏版》