7.2 客户区鼠标消息

当鼠标移过窗口的显示区域时,窗口消息处理程序收到WM_MOUSEMOVE消息。当在窗口的显示区域中按下或者释放一个鼠标按键时,窗口消息处理程序会接收到下面这些消息:

按下

释放

按下(双键)

WM_LBUTTONDOWN

WM_LBUTTONUP

WM_LBUTTONDBLCLK

WM_MBUTTONDOWN

WM_MBUTTONUP

WM_MBUTTONDBLCLK

WM_RBUTTONDOWN

WM_RBUTTONUP

WM_RBUTTONDBLCLK

要接受到双键消息,需要在wndclass.style处增加CS_DBLCLKS

在WM_MOUSEMOVE中,可以用LOWORD和HIWORD宏来提取鼠标的位置:

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

wParam中包含信息。MK前缀代表「鼠标按键」。

MK_LBUTTON

按下左键

MK_MBUTTON

按下中键

MK_RBUTTON

按下右键

MK_SHIFT

按下Shift键

MK_CONTROL

按下Ctrl键

当您把鼠标移过窗口的显示区域时,Windows并不为鼠标的每个可能的图素位置都产生一个WM_MOUSEMOVE消息。您的程序接收到WM_MOUSEMOVE消息的次数,依赖于鼠标硬件,以及您的窗口消息处理程序在处理鼠标移动消息时的速度。

  1. 窗口消息处理程序可以「拦截鼠标」并且连续地接收鼠标消息,即使此时鼠标在该窗口显示区域之外。您将在本章的后面学习如何拦截鼠标。
  2. 如果正在显示一个系统模态消息框或者系统模态对话框,那么其它程序就不能接收鼠标消息。当系统模态消息框或者对话框活动时,禁止切换到其它窗口或者程序。一个显示系统模态消息框的例子,是当您关闭Windows时。

实例

#include<windows.h>

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

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("Mouse Move");
HWND hwnd;
MSG msg;
WNDCLASS wndclass; wndclass.hInstance = hInstance;
wndclass.lpfnWndProc = WndProc;
wndclass.lpszClassName = szAppName;
wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.cbClsExtra = ;
wndclass.cbWndExtra = ;
wndclass.lpszMenuName = ; if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("错误, 无法注册窗口类."), TEXT("错误"), MB_OK);
return ;
} hwnd = CreateWindow(szAppName, TEXT("Mouse Move"),
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL); ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd); while (GetMessage(&msg, NULL, , ))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
} return msg.wParam;
} LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static POINT pt[];
HDC hdc;
static int iCount;
PAINTSTRUCT ps;
int i, j; switch (message) {
case WM_LBUTTONDOWN:
iCount = ;
InvalidateRect(hwnd, NULL, TRUE);//重画时擦除背景
return ;
case WM_MOUSEMOVE:
if (wParam && MK_LBUTTON && iCount < ) {
pt[iCount].x = LOWORD(lParam);
pt[iCount++].y = HIWORD(lParam);
hdc = GetDC(hwnd);
SetPixel(hdc, LOWORD(lParam), HIWORD(lParam), );
ReleaseDC(hwnd, hdc);
}
return ;
case WM_LBUTTONUP:
InvalidateRect(hwnd, NULL, FALSE);//重画时不擦除背景
return ;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
SetCursor(LoadCursor(NULL, IDC_WAIT));
ShowCursor(TRUE);//显示鼠标
for (i = ; i < iCount - ; ++i) {
for (j = i + ; j < iCount; ++j) {
MoveToEx(hdc, pt[i].x, pt[i].y, NULL);
LineTo(hdc, pt[j].x, pt[j].y);
}
}
SetCursor(LoadCursor(NULL, IDC_ARROW));
ShowCursor(FALSE);//隐藏鼠标
EndPaint(hwnd, &ps);
} return DefWindowProc(hwnd, message, wParam, lParam);
}

7.2.2 处理shift

组合键测试

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WM_MOUSEMOVE:
if (wParam & MK_CONTROL) {
if (wParam & MK_SHIFT) {
MessageBox(hwnd, TEXT("按下Ctrl+Shift键"), TEXT("test!"), NULL);
return ;
}
}
} return DefWindowProc(hwnd, message, wParam, lParam);
}

7.2.3 鼠标双击

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WM_LBUTTONDBLCLK:
MessageBox(hwnd, TEXT("左键双击"), TEXT("test!"), NULL);
return ;
} return DefWindowProc(hwnd, message, wParam, lParam);
}

客户区鼠标消息简单应用

#include<windows.h>

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

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("Mouse Move");
HWND hwnd;
MSG msg;
WNDCLASS wndclass; wndclass.hInstance = hInstance;
wndclass.lpfnWndProc = WndProc;
wndclass.lpszClassName = szAppName;
wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.cbClsExtra = ;
wndclass.cbWndExtra = ;
wndclass.lpszMenuName = ; if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("错误, 无法注册窗口类."), TEXT("错误"), MB_OK);
return ;
} hwnd = CreateWindow(szAppName, TEXT("Mouse Move"),
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
, ,
NULL, NULL, hInstance, NULL); ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd); while (GetMessage(&msg, NULL, , ))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
} return msg.wParam;
} LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static int cxClient, cyClient;
static POINT point;
HDC hdc;
PAINTSTRUCT ps;
TCHAR szBuffer[]; switch (message) {
case WM_SIZE:
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
return ;
case WM_MOUSEMOVE:
GetCursorPos(&point);
InvalidateRect(hwnd, NULL, TRUE);
return ;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
TextOut(hdc, cxClient / - , cyClient / - , szBuffer, wsprintf(szBuffer, TEXT("屏幕坐标(%d, %d)"), point.x, point.y));
ScreenToClient(hwnd, &point);
TextOut(hdc, cxClient / - , cyClient / , szBuffer, wsprintf(szBuffer, TEXT("客户区坐标(%d, %d)"), point.x, point.y));
EndPaint(hwnd, &ps);
return ;
} return DefWindowProc(hwnd, message, wParam, lParam);
}

7.3 非客户区鼠标消息

按下

释放

按下(双击)

WM_NCLBUTTONDOWN

WM_NCLBUTTONUP

WM_NCLBUTTONDBLCLK

WM_NCMBUTTONDOWN

WM_NCMBUTTONUP

WM_NCMBUTTONDBLCLK

WM_NCRBUTTONDOWN

WM_NCRBUTTONUP

WM_NCRBUTTONDBLCLK

可以用两个Windows函数将屏幕坐标转换为显示区域坐标或者反之:

ScreenToClient (hwnd, &pt) ;
ClientToScreen (hwnd, &pt) ;

7.3.1 击中测试消息

WM_NCHITTEST,它代表「非显示区域命中测试」。此消息优先于所有其它的显示区域和非显示区域鼠标消息。lParam参数含有鼠标位置的x和y屏幕坐标,wParam 参数另有用途。

Windows应用程序通常把这个消息传送给DefWindowProc,然后Windows用WM_NCHITTEST消息产生与鼠标位置相关的所有其它鼠标消息。对于非显示区域鼠标消息,在处理WM_NCHITTEST时,从DefWindowProc传回的值将成为鼠标消息中的wParam参数,这个值可以是任意非显示区域鼠标消息的wParam值再加上以下内容:

HTCLIENT

HTNOWHERE

HTTRANSPARENT

HTERROR

显示区域

不在窗口中

窗口由另一个窗口覆盖

使DefWindowProc产生警示用的哔声

如果DefWindowProc在其处理WM_NCHITTEST消息后传回HTCLIENT,那么Windows将把屏幕坐标转换为显示区域坐标并产生显示区域鼠标消息。

#define HTERROR             (-2)                    //在屏幕的后面或在窗体之间的线上(使函数DefWindowProc产生一个警示音)
#define HTTRANSPARENT (-1) //在一个被其它窗口覆盖的窗口中
#define HTNOWHERE 0 //在屏幕背景或窗口之间的分界线
#define HTCLIENT 1 //在客户区中
#define HTCAPTION 2 //在标题栏中
#define HTSYSMENU 3 //在一个窗口菜单栏或子窗口的关闭按钮上
#define HTGROWBOX 4 //在尺寸框中
#define HTSIZE HTGROWBOX //同HTGROWBOX
#define HTMENU 5 //在菜单区域
#define HTHSCROLL 6 //在水平滚动条上
#define HTVSCROLL 7 //在垂直滚动条上
#define HTMINBUTTON 8 //在最小化按钮上
#define HTMAXBUTTON 9 //在最大化按钮上
#define HTLEFT 10 //在窗口的左边框上
#define HTRIGHT 11 //在窗口的右边框上
#define HTTOP 12 //在窗口水平边框的上方
#define HTTOPLEFT 13 //在窗口边框的左上角
#define HTTOPRIGHT 14 //在窗口边框的右上角
#define HTBOTTOM 15 //在窗口的水平边框的底部
#define HTBOTTOMLEFT 16 //在窗口边框的左下角
#define HTBOTTOMRIGHT 17 //在窗口边框的右下角
#define HTBORDER 18 //在不具有可变大小边框的窗口的边框上
#define HTREDUCE HTMINBUTTON //同HTMINBUTTON
#define HTZOOM HTMAXBUTTON //同HTMAXBUTTON
#define HTSIZEFIRST HTLEFT //同HTLEFT
#define HTSIZELAST HTBOTTOMRIGHT //同HTBOTTOMRIGHT
#define HTOBJECT 19 //忽略该标识符, 已废弃
#define HTCLOSE 20 //在关闭按钮上
#define HTHELP 21 //在帮助按钮上
#include<windows.h>

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

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("Mouse Move");
HWND hwnd;
MSG msg;
WNDCLASS wndclass; wndclass.hInstance = hInstance;
wndclass.lpfnWndProc = WndProc;
wndclass.lpszClassName = szAppName;
wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.cbClsExtra = ;
wndclass.cbWndExtra = ;
wndclass.lpszMenuName = ; if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("错误, 无法注册窗口类."), TEXT("错误"), MB_OK);
return ;
} hwnd = CreateWindow(szAppName, TEXT("Mouse Move"),
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
, ,
NULL, NULL, hInstance, NULL); ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd); while (GetMessage(&msg, NULL, , ))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
} return msg.wParam;
} LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
TCHAR szBuffer[];
static POINT pt; switch (message) {
case WM_NCLBUTTONDOWN:
pt.x = LOWORD(lParam);
pt.y = HIWORD(lParam);
ScreenToClient(hwnd, &pt);
switch (wParam) {
case HTCAPTION:
wsprintf(szBuffer, TEXT("击中标题栏,客户区坐标(%d, %d)"), pt.x, pt.y);
MessageBox(hwnd, szBuffer, TEXT("test"), NULL);
break;
case HTMINBUTTON:
wsprintf(szBuffer, TEXT("击中最小化,客户区坐标(%d, %d)"), pt.x, pt.y);
MessageBox(hwnd, szBuffer, TEXT("test"), NULL);
break;
case HTMAXBUTTON:
wsprintf(szBuffer, TEXT("击中最大化,客户区坐标(%d, %d)"), pt.x, pt.y);
MessageBox(hwnd, szBuffer, TEXT("test"), NULL);
break;
}
return ;
} return DefWindowProc(hwnd, message, wParam, lParam);
}

Windows程序设计(七)--鼠标的更多相关文章

  1. Windows程序设计学习笔记(1):一个简单的windows程序

    <Windows程序设计>(第五版)(美Charles Petzold著) #include<windows.h> LRESULT CALLBACK WndProc(HWND, ...

  2. Windows 程序设计(4) MFC 03 -系列学习

    本文整体目录和绝大部门内容来自 [鸡啄米网站]的MFC系列文章,欢迎支持原创 (一)VS2010/MFC编程入门之前言 VC++全称是Visual C++,是由微软提供的C++开发工具,它与C++的根 ...

  3. Windows 程序设计

    一.Win32 API /******************************************************************** created: 2014/04/1 ...

  4. 关于《Windows程序设计(第五版)》中一个实例程序的疑问

    最近一直在看Charlse Petzold的<Windows程序设计>,作为一个新得不能再新的新手,只能先照着书的抄抄源码了,之前的例子一直都很正常,但昨天遇到一个很诡异的BUG. 先看实 ...

  5. windows 程序设计自学:添加图标资源

    #include <windows.h> #include "resource.h" LRESULT CALLBACK MyWndProc( HWND hwnd, // ...

  6. windows程序设计笔记

    2014.05.06 新建一个visual C++ -- 常规 -- 空白 的项目,用.c后缀名指定这是一个用C语言来写的windows项目.和C语言的hellworld程序做了一个比较,按照wind ...

  7. 《Windows程序设计第5版》学习进度备忘

    书签:另外跳过的内容有待跟进 __________________学习资源: <Windows程序设计第5版珍藏版> __________________知识基础支持: _________ ...

  8. MFC Windows程序设计源代码免费下载

    本人近期在网上找到了<MFC Windows程序设计>第二版的书内程序的源代码,特意上传CSDN上面,供学习MFC的程序猿们免费下载. 源代码下载: http://download.csd ...

  9. windows 程序设计 SetPolyFillMode关于ALTERNATE、WINDING的详细解释

    看windows程序第五章GDI编程部分.一直卡壳在这里了. 下面我来说下自己的想法.看是否对您有帮助. 首先我们来看一个图. SetPolyFillMode(ALTERNATE);  // 系统默认 ...

随机推荐

  1. 22pygame 安装

    实战步骤 pygame 快速体验 飞机大战 实战 确认模块 --pygame pygame 就是一个 Python 模块, 专为电子游戏设计 提示 : 学习第三方模块, 通常最好的参考资料就在官方网站 ...

  2. 记一次 gunicorn 启动 flask 出问题的经历

    出错现象: gunicorn+nginx+flask 部署项目, 部署过程没问题,项目也正常启动了,但是一旦访问接口,就会报错: Traceback (most recent call last): ...

  3. [CF] 8C Looking for Order

    状压模板题 CF难度2000? 我得好好了解一下CF的难度机制了 反正CF的难度比洛谷真实就好了 Code #include<algorithm> #include<iostream ...

  4. CF dp 题(1500-2000难度)

    前言 从后往前刷 update 新增 \(\text{\color{red}{Mark}}\) 标记功能,有一定难度的题标记为 \(\text{\color{red}{红}}\) 色. 题单 (刷过的 ...

  5. HTML表单(来自MDN的总结)

    表单介绍 HTML表单是用户和web站点或应用程序之间交互的主要内容之一.它们允许用户将数据发送到web站点.大多数情况下,数据被发送到web服务器,但是web页面也可以拦截它自己并使用它. HTML ...

  6. 前端自动化gulp使用方法

    gulp介绍 1. 网站: http://slides.com/contra/gulp#/ 2. 特点 易于使用:通过代码优于配置的策略, Gulp 让简单的任务简单,复杂的任务可管理. 构建快速 : ...

  7. tuple写法

    name = ("wen") 类型为strname = ("wen",) 类型为tuple

  8. 【CF1243B2】Character Swap (Hard Version)【思维】

    题意:给定两个字符串,问是否存在交换方案使得两个字符串相同,方案为交换次数小于等于2n,且每次只交换s1与s2中的一个字符 题解:考虑从前往后枚举,当第i位不同时,考虑找后边的第j位,若存在这样的第j ...

  9. java 中的运算符

    Java的运算符,分为四类: 算数运算符.关系运算符.逻辑运算符.位运算符. 算数运算符():+ - * / % ++ -- 关系运算符():== != > >= < <= 逻 ...

  10. 2017华南理工华为杯H bx值(容斥问题)

    题目描述 对于一个nnn个数的序列 a1,a2,⋯,ana_1,a_2,\cdots,a_na​1​​,a​2​​,⋯,a​n​​,从小到大排序之后为ap1,ap2,⋯,apna_{p_1},a_{p ...