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. CF dp 题(1500-2000难度)

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

  2. 【vue】vue不足 待补强

    83719279 9:56:03尤其是路由 声明周期 父子通信 组件通信 以及钩子函数83719279 9:56:17这些我都不敢用,只能用最原始的方法83719279 9:56:32还有es6 js ...

  3. R语言 Keras Training Flags

    在需要经常进行调参的情况下,可以使用 Training Flags 来快速变换参数,比起直接修改模型参数来得快而且不易出错. https://tensorflow.rstudio.com/tools/ ...

  4. 【leetcode】1091. Shortest Path in Binary Matrix

    题目如下: In an N by N square grid, each cell is either empty (0) or blocked (1). A clear path from top- ...

  5. 【leetcode】816. Ambiguous Coordinates

    题目如下: 解题思路:我的方案是先把S拆分成整数对,例如S='1230',先拆分成(1,230),(12,30),(123,0),然后再对前面整数对进行加小数点处理.比如(12,30)中的12可以加上 ...

  6. 简单的使用redis

    心不慌手不抖我们跟着大哥走 https://blog.csdn.net/zhangcongyi420/article/details/82686702

  7. OC中保存自定义类型对象的持久化方法

    OC中如果要将自定义类型的对象保存到文件中,必须进行以下三个条件: 想要把存放自定义类型的数组进行 持久化(就是将内存中的临时数据以文件<数据库等>的形式写到磁盘上)必须满足: 1. 自定 ...

  8. 阿里云祝顺民(江鹤):云原生SDWAN加速企业上云 引领未来智能网络

    第二届中国SD-WAN峰会于11月16日在北京盛大开幕,阿里云以黄金赞助商之名隆重参与.作为全球前三,亚太第一的云计算厂商,阿里云一直引领云网技术的演进及应用落地.过去一年,阿里云发布以云为中心的云原 ...

  9. c#代码规则,C#程序中元素的命名规范

    俩种命名方法 1.Pascal 命名法,第一个字母大写其它字母小写Userid 2.Camel命名法,所有单第一方写大写,其它小写,骆峰命名法,userId C#程序中元素的命名规范项目名:公司名.项 ...

  10. 【HDOJ6635】Nonsense Time(时间倒流,lis)

    题意:给定n个数的数列,第i个数为a[i],刚开始所有位置都处于禁用状态,第i次之后位置p[i]变为可用,求每次变化后的lis长度 n,a[i],p[i]<=5e4 保证a[i],p[i]均为随 ...