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. mongodb 用户 权限 设置 详解

    原文地址:http://blog.51yip.com/nosql/1575.html 我知道的关系型数据库都是有权限控制的,什么用户能访问什么库,什么表,什么用户可以插入,更新,而有的用户只有读取权限 ...

  2. jenkins解决python不是内部命令

    1.在 Windows 提示符下运行是没有问题. 2.把Jenkins项目配置中 python main.py   修改成python可执行文件全路径:D:\Python35\python.exe m ...

  3. web选择文件夹上传

    我们平时经常做的是上传文件,上传文件夹与上传文件类似,但也有一些不同之处,这次做了上传文件夹就记录下以备后用. 这次项目的需求: 支持大文件的上传和续传,要求续传支持所有浏览器,包括ie6,ie7,i ...

  4. 实现网页qq在线咨询功能

    在自己的网页中实现qq在线咨询,给图片或链接添加地址为:tencent://message/?uin=你的qq号码!就可以了. <a href="tencent://message/? ...

  5. android设置系统默认开机时间

    1.设置RTC时间,该时间是如果RCT时钟断电以后使用的默认时间 Android L之前: \alps\mediatek\custom\[project]\preloader\ inc\cust_rt ...

  6. What is httpcontext

    https://docs.microsoft.com/en-us/dotnet/api/system.web.httpcontext?view=netframework-4.8 Encapsulate ...

  7. brdd 惰性执行 mapreduce 提取指定类型值 WebUi 作业信息 全局临时视图 pyspark scala spark 安装

    [rdd 惰性执行] 为了提高计算效率 spark 采用了哪些机制 1-rdd 基于分布式内存数据集进行运算 2-lazy evaluation  :惰性执行,即rdd的变换操作并不是在运行该代码时立 ...

  8. spring boot创建多模块聚合工程

    环境:java1.8,idea 聚合工程优势: 1.统一maven操作.可以在一个maven工程管理多个子工程(每个子工程可单独打包,重启,调试.也可通过聚合工程一起管理). 2.统一管理依赖版本.可 ...

  9. Dos.Common - 目录、介绍

    引言: Dos.Common是一个开发中的常用类库,如HttpHelper.LogHelper.CacheHelper.CookieHelper.MapperHelper等等.与Dos.WeChat. ...

  10. c++内存布局与c程序的内存布局

    c/c++的内存布局:堆,栈,自由存储区(与堆的区别),全局/静态存储区,常量存储区(字符串常量,const常量) http://www.cnblogs.com/QG-whz/p/5060894.ht ...