目录
0x00什么叫做消息?
win32中用一个MSG结构体来描述一个消息,在MSG结构体中有以下4个成员变量:
- hWnd 窗口句柄,指明消息属于哪个窗口
- 消息的本质 指明消息属于鼠标消息,键盘消息还是其他消息
- lParam 消息的附加信息
- wParam 消息的附加信息
0x01消息从哪里来?要到哪里去?
windows操作系统中,发生任何一个事件,操作系统都会封装成相应的消息,然后将消息存入消息队列,然后发送给窗口应用程序,窗口应用程序中的“消息循环”接收消息,翻译消息,派发消息给自己的“消息处理函数”,“消息处理函数”中的swith case语句针对不同的消息,产生不同的响应。
PS:有些消息,会跳过存入消息队列这一步。
0x02为什么需要翻译消息呢?
因为操作系统会将相应的消息封装成结构体,这个结构体窗口应用程序不能理解,所以需要将操作系统封装的结构体“翻译”成为窗口应用程序能够处理的MSG结构体。通俗的讲,翻译消息就是格式转换。
0x03派发消息的具体过程是怎样的?
一个应用程序有很多窗口,每一个窗口都有自己的“消息处理函数”,派发消息就是将消息发生给各个窗口,窗口接收到消息之后,就会调用窗口处理函数 WndProc
0x04 消息的“周边新闻”:
消息队列分为两种:
- 操作系统来维护的消息队列(OS message queue):鼠标消息,键盘消息
- 窗口应用程序来维护的消息队列:存放当前应用程序的消息,例如:WM_PAINT绘图
消息的分类:
分类1:
系统消息:操作系统已经定义好的消息,一般以WM_开头(windows message),所有消息的本质都是一个整数,系统消息整数的范围为0-0x3FF 总共1024个,0x3FF == 1023
用户消息(自定义消息):WM_USER 0x400-0x7FF 1024-2047
分类2:
队列消息:存储和取出都要经过消息队列。例如:WM_PAINT WM_MOUSE。
非队列消息:这些消息不需要存储到消息队列,直接被发送给窗口应用程序。例如:
- WM_CREATE:窗口被创建后,显示前发送的消息。即CreateWindow函数返回的那一刹那(ShowWindow之前),就会产生一个WM_CREATE消息,这个消息不用存入消息队列再出队,直接被发送给窗口应用程序处理。
- WM_SIZE:窗口大小被改变的时候,产生并发送的消息。
MSG msg;//声明一个MSG类型的消息结构体
//消息循环是一个死循环,永远不会结束,除非。。。你写一个语句break
while (1) {
//获取消息
//GetMessage(&msg, NULL, NULL, NULL);//GetMessage函数是一个阻塞函数,如果接收一个消息所用的时间过长,在此期间内,如果有另一个消息被发送,就会接受不到这个消息
if(PeekMessage(&msg, NULL, NULL,NULL,NULL))//最后一个参数表示是否删除消息
/*PeekMessage的工作原理:非阻塞方式接受消息,主要查看是否有消息,不会将消息存入MSG结构体中,GetMessage函数负责将消息存入MSG结构体中。
Peekmessage会检查消息队列,如果消息队列中有消息,函数就会查看最后一个参数,
如果最后一个参数为false或者NULL,直接返回1
如果最后一个参数为true,从消息队列中删除消息,然后返回1
如果消息队列中没有消息,直接返回0
*/{
if(0 == GetMessage(&msg, NULL, NULL, NULL)) break;
/*GetMessage工作原理:
GetMessage从消息队列中获取一个消息存放到MSG结构体中。
如果消息队列中有消息,把消息存入MSG,删除当前程序消息队列中的消息,返回非0
直到遇到WM_QUIT消息才返回0.注意:关闭窗口不会发送WM_QUIT消息,只会发送WM_DESTROY消息,只有在消息处理函数中定义当收到WM_DESTROY时,向消息队列中添加WM_QUIT消息,
才能通过if(0 == GetMessage(&msg, NULL, NULL, NULL)) break;退出循环。
如果当前程序消息队列中没有消息,就去系统消息队列中看有没有消息。如果系统消息队列
里面有消息,就把系统消息队列中的消息放入当前程序消息队列。如果系统消息队列中没有消息,就
检查窗口是否需要重新绘制。如果需要重新绘制,操作系统发送WM_PAINT消息,如果不需要重新绘制
查看是否有定时器,如果有定时器,处理定时器消息,如果没有定时器,优化资源,处理内存,继续等待。
消息循环处理消息的次数可能小于消息处理函数处理消息的次数,因为其他应用程序的消息也可能发送给消息处理函数处理。
*/
/*
if (msg.message == WM_QUIT) { //WM_QUITE消息表示整个窗口退出
break;
}
*/
//翻译消息
TranslateMessage(&msg);//翻译消息
/*消息的翻译主要针对键盘消息,键盘消息只有两个:
WM_TDOWN
WM_TUP
if(msg.message==WM_KEY 按键消息){
if(是否可见字符){//可见字符即代表字符有回显,例如F1F2..PgUp等不是可见字符。
//如果是可见字符,代表应该告诉应用程序,用户按键是用来编辑,发送的是一个字符,否则,就认为用户按键是发送一个指令。
//如果用户发送的是一个字符,就需要翻译一下
if(Caps Lock是否按下){
PostMessage(大写字母消息);//发送大写字母消息
}else{
PostMessage(小写字母消息);//发送小写字母消息
}
}
}
return;
*/
DispatchMessage(&msg);//派发消息,群发消息,给当前所有的应用程序都发送。而PostMessage表示只对当前应用程序的消息处理函数发送消息。
}
}
//6.消息循环 循环 接受消息 翻译消息 派发消息
0x05如何使用资源:
资源:程序中使用的其他东西,包括图片,对话框,光标,图标等
- 创建资源文件 resources.h ****.rc 鼠标右键添加资源即可
- 导入资源:鼠标右键-》添加资源->导入->选择资源文件->确定
- 获取资源id:可以从资源视图,或者从resources.h文件中获取
- 资源使用:
- 导入资源文件.rc
- 加载资源 句柄 = LoadImage()作用是将资源从一个文件变成一个变量。
- 使用资源 SetClassLong
LoadImage函数:返回值为所加载资源的句柄
HANDLE LoadImageA(
HINSTANCE hInst,//当前应用程序实例句柄
LPCSTR name,//资源名字
UINT type,//资源类型
int cx,int cy,//大小
UINT fuLoad);//加载方式
例如加载一个光标资源:
HCURSOR g_hCursorRight=LoadImage(g_hInstance,MAKEINTRESOURCE(IDC_RIGHT),IMAGE_CURSOR,32,32,LR_LOADTRANSPARENT);
这里资源的名字 就是光标的ID,需要查看资源文件才能知道,并且这个需要强制类型转化,这个宏的意思是将一个数字类型转化为指针类型。资源类型IMAGE_CURSOR,光标图片,后面两位数字是光标图片的长和宽,这个也是需要查看资源文件才能知道我们加载的光标图片的大小是多少。最后一个是加载资源的方式:有两种,一种是LR_LOADFROMFILE 文件方式加载,另一种是LR_LOADFROMTRANSPARENT 资源方式。
如果以文件方式加载资源文件,第二参数就直接写资源文件的名字
如果以资源方式加载资源文件,第二参数就需要写资源文件的ID,并且强转成指针类型。
设置光标:
DWORD SetClassLong(
HWND hWnd, // 当前应用程序实例句柄
int nIndex, // index of value to change (设置的属性) == 资源的类型
LONG dwNewLong // new value (设置的属性的值) == 被使用的资源的句柄
);
例如:我们让鼠标只要一移动,就设置光标的样子为g_hCursorLeft对应的样子。
void OnMouseMove(HWND hwnd){
SetClassLong(hwnd,GCL_HCURSOR,(LONG)g_hCursorLeft);
//SetClassLong(hwnd,GCL_HCURSOR,(LONG)g_hCursorRight);
return;
}
//在消息处理函数中
case WM_MOUSEMOVE:
OnMouseMove(hwnd);
break;
0x06如何在window窗口程序中使用命令行窗口:
- 得到命令行窗口使用权限 AllocConsole
得到命令行窗口句柄 GetStdHandle(要得到三大句柄中的那个句柄),windows程序中你要操控一个什么东西,一般都是需要这个东西的句柄。其中 std表示标准,命令行窗口由三大部分组成:标准输入设备,标准输出设备,标准错误输出设备。因此微软定义了三个宏常量,注意这只是三个DWORD类型的宏常量,不是HANDLE类型,这三个宏常量传入GetStdHandle之后,才会返回对应的HANDLE类型(说白了就是void*类型)
#define STD_INPUT_HANDLE ((DWORD)-10) //标准输入句柄
#define STD_OUTPUT_HANDLE ((DWORD)-11) //标准输出句柄
#define STD_ERROR_HANDLE ((DWORD)-12) //标准错误输出句柄
- 说白了:微软的搞来搞去
- HANDLE 句柄 就是 void* 类型
- HWND 窗口句柄就是int*
- HHOOK 钩子句柄 float*
- HFILE 文件句柄 double*
- HCORSOR 光标句柄
- 使用命令行窗口
- WriteConsole 向控制台写
- ReadConsole 从控制台读
0x07 封装MyPrintf()
win32程序中命令行窗口调试程序非常方便,所以我将命令行窗口的代码封装成了一个MyPrintf()函数。
template <typename T>
void MyPrintf(char* str,T data){
AllocConsole();
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE);
//HANDLE hError = GetStdHandle(STD_ERROR_HANDLE);
char buff[1024]={0};
sprintf(buff,str,data);
WriteConsole(hOutput,buff,strlen(buff),NULL,NULL);
return;
}
0x08 通过控制台输出当前窗口的大小
一般程序会在收到WM_CREATE消息之后,来调用命令行窗口。并且一般会将调用命令行窗口的代码封装为一个函数OnCreate,即对WM_CREATE,WM_SIZE需要进行的操作进行封装。
如何获取窗口的大小?
GetWindowRect(窗口实例句柄,&RECT结构体变量) 该函数可以获取窗口实例句柄对应的窗口的上下左右的坐标信息。并将这些信息存入参数中RECT类型的结构体中。我们只要知道了窗口上下左右的坐标信息,就可以推算出窗口的长和宽了。
#include <windows.h>
#include <stdio.h>
HANDLE outputHandle, inputHandle,errorHandle;
//WM_CREATE消息的处理函数
void OnCreate() {
AllocConsole();
outputHandle = GetStdHandle(STD_OUTPUT_HANDLE);
inputHandle = GetStdHandle(STD_INPUT_HANDLE);
errorHandle = GetStdHandle(STD_ERROR_HANDLE);
}
//WM_SIZE消息的处理函数
void OnSize(HWND hWnd) {
RECT leftTopRightBottom;
GetWindowRect(hWnd, &leftTopRightBottom);
char buff[1024] = { 0 };
sprintf(buff, "窗口宽:%d,窗口长:%d", leftTopRightBottom.left - leftTopRightBottom.right, leftTopRightBottom.top - leftTopRightBottom.bottom);
WriteConsole(outputHandle, buff, strlen(buff), NULL, NULL);
return;
}
LRESULT CALLBACK WndProcessFunction(HWND hWnd, UINT code, WPARAM wParam, LPARAM lParam) {
switch (code) {
case WM_CREATE:
OnCreate();
break;
case WM_DESTROY:
MessageBox(hWnd, L"窗口关闭了!", L"警告!",MB_OK);
break;
case WM_SIZE://每当窗口大小改变时,打印当前窗口大小
default:
return DefWindowProc(hWnd, code, wParam, lParam);
}
return 0;
}
int WinMain(
HINSTANCE hInstance,
HINSTANCE hParentInstance,
LPSTR lpCmd,
int len
) {
WNDCLASSEX myWindow;
myWindow.cbClsExtra = NULL;
myWindow.cbSize = sizeof(WNDCLASSEX);
myWindow.cbWndExtra = NULL;
myWindow.hbrBackground = (HBRUSH)GetStockObject(5);
myWindow.hCursor = NULL;
myWindow.hIcon = NULL;
myWindow.hIconSm = NULL;
myWindow.hInstance = hInstance;
myWindow.lpfnWndProc = WndProcessFunction;
myWindow.lpszClassName = L"不知道叫个什么名字好的类";
myWindow.lpszMenuName = L"菜单没有什么名字";
myWindow.style = CS_VREDRAW;
RegisterClassEx(&myWindow);
HWND handle = CreateWindow(myWindow.lpszClassName, L"几把窗口", WS_OVERLAPPED, 300, 300, 100, 100, NULL, NULL, hInstance, NULL);
ShowWindow(handle, SW_HIDE);
UpdateWindow(handle);
MSG msg;
while (1) {
GetMessage(&msg, NULL, NULL, NULL);
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
0x08 鼠标消息:小任务光标变化
需求:
如果光标在窗口左边,设置为试管
如果光标在窗口右边,设置为 I
1.需要知道现在鼠标动了没有 WM_MOUSEMOVE消息 鼠标移动动作 操作系统就会封装该消息发送到任意一个窗口
2.知道鼠标在窗口的左边还是右边
相对坐标:当前窗口坐标,鼠标的相对坐标在鼠标消息MSG的lParam里面
绝对坐标:windows桌面坐标
3.根据鼠标位置设置光标
鼠标消息:
窗口所围成的区域称为“客户区”,只有鼠标在客户区移动时,才会触发鼠标消息WM_MOUSEMOVE。
光标的位置坐标有两类:
1.屏幕坐标:相对与整个屏幕的坐标
2.客户区坐标:相对于客户区的坐标
- lParam的高16位 y坐标 int y = HIWORD(lParam);
- lParam的第16位 x坐标 int x = LOWORD(lParam);
- 注意:这里有坑,GetWindowRect(hwnd,rect) 获取的是窗口上下左右的屏幕坐标,而lParam中存的却是客户区坐标,因此,在判断光标位于客户区左还是右时,需要算出客户区中心的客户区坐标。
客户区坐标与屏幕坐标之间的相互转化:
#include <windows.h>
POINT pos;
pos.x = HIWORD(lParam);
pos.y =LOWORD(lParam);
ClientToScreen(hwnd,&pos);
//此后pos中存的就是屏幕坐标了
当前光标的位置坐标是由鼠标消息(MSG类型的结构体)的lParam(附加消息)携带的。
思路:
首先,我们做好准备工作:当窗口创建成功时,加载好两个光标资源。
然后解析鼠标消息,获得当前光标的坐标,除此之外,还需要知道当前窗口中心位置的x坐标 = 窗口.left + (窗口的宽)/2
如果光标的x坐标大于窗口中心位置的x坐标,就把光标设置为光标1
否则就设置为光标2.
代码改进版:只记录了消息处理函数部分
#include <windows.h>
HANDLE g_hOutput;
HANDLE g_hCursorLeft,g_hCursorRight;
void OnCreate(){
//加载光标资源
LoadImage()
//把命令行窗口也准备好
AllocConsole();
g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
return;
}
void OnMousemove(){
//判断当前鼠标在哪里
//根据位置设置光标
return;
}
/* This is where all the input to the window goes to */
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
switch(Message) {
/* trap the WM_CLOSE (clicking X) message, and actually tell the window to close */
case WM_CLOSE: {
DestroyWindow(hwnd);
break;
}
/* Upon destruction, tell the main thread to stop */
case WM_DESTROY: {
PostQuitMessage(0);
break;
}
case WM_Create:
OnCreate();
break;
case WM_MOUSEMOVE:
OnMousemove();
break;
/* All other messages (a lot of them) are processed using default procedures */
default:
return DefWindowProc(hwnd, Message, wParam, lParam);
}
return 0;
}
代码原版:
#include <windows.h>
#include "stdafx.h"
#include "Resource.h"
#include <stdio.h>
#include <cstdio>
#include <string.h>
#include <cstring>
HINSTANCE g_hInstance;
HCURSOR g_hCursor1, g_hCursor2;//光标句柄
MSG msg;//声明一个MSG类型的消息结构体
HANDLE g_hConsole;//定义一个HANDLE类型的全局变量,之后要将命令行窗口句柄赋值给它,命名时养成习惯:如果是全局变量以g_开头命名它。
void On_MouseMove(HWND hWnd, WPARAM wParam, LPARAM lParam) {
int y = HIWORD(lParam);//lParam是long类型,32个位(两个字单元)来存储鼠标的相对坐标,其中高16位为y坐标,低16位为x坐标
int x = LOWORD(lParam);//HIWORD()和LOWORD()为宏函数,分别代表取高16位,和取低16位
char buff[256] = { 0 };
sprintf_s(buff,"(%d,%d)\n",x,y);//将格式化数据"(%d,%d)\n"写入buff指向的字符数组。
WriteConsole(g_hConsole, buff, strlen(buff), NULL, NULL);//使用命令行写出数据
// 命令行窗口句柄,字符数组首地址,写出字符串长度,写入字节的指针,保留的。。。
//拿到窗口的大小
RECT rect;//RECT类型的结构体
/*
typedef struct _RECTL
{
LONG left;//左边缘的x坐标
LONG top;//上边缘的y坐标
LONG right;//右边缘的x坐标
LONG bottom;//下边缘的y坐标
} RECTL, *PRECTL, *LPRECTL;
*/
GetWindowRect(hWnd, &rect);//这个函数能拿到一个窗口的矩形信息,并且把矩形信息存入到矩形rect结构体中
int w = rect.right - rect.left;//宽=右-左
int h = rect.bottom - rect.top;//高=底部-顶部
if (x < w / 2) {
//左边
SetClassLong(hWnd, GCL_HCURSOR, (LONG)g_hCursor1);//通过窗口句柄去设置窗口类,GCL_HCURSOR表示设置光标,第三个参数表示设置哪一个光标
}
else {
//右边
SetClassLong(hWnd, GCL_HCURSOR, (LONG)g_hCursor2);
}
}
//写一个windows窗口
//5定义消息处理函数
LRESULT CALLBACK WndProc(HWND hWnd,//窗口句柄
//CALLBACK表示是一个回调函数,LRESULT是函数返回值的类型,可以通过查看wc.lpfnWndProc的定义查看
UINT code,//消息 msg.message
WPARAM wParam,//消息附加信息
LPARAM lParam//消息附加信息
) {
//用switch case 来判断是什么消息
switch (code) {
case WM_MOUSEMOVE:
On_MouseMove(hWnd,msg.wParam,msg.lParam);
break;
case WM_CREATE://代表窗口创建消息
g_hCursor1=(HCURSOR)LoadImage(g_hInstance, MAKEINTRESOURCE(IDC_CURSOR1), IMAGE_CURSOR, 32, 32, LR_LOADTRANSPARENT);
g_hCursor2= (HCURSOR)LoadImage(g_hInstance, MAKEINTRESOURCE(IDC_CURSOR2), IMAGE_CURSOR, 32, 32, LR_LOADTRANSPARENT);
break;
case WM_PAINT://绘图消息
break;
case WM_SYSCOMMAND://窗口的关闭,放大,缩小动作都会产生WM_COMMAND消息
MessageBox(NULL, "发送了WM_COMMAND消息","提示", MB_OK);
break;
case WM_DESTROY:
PostQuitMessage(0);//PostQuitMessage函数发送一个WM_QUIT消息到线程消息队列并且立即返回
break;
default:
return DefWindowProc(hWnd, code, wParam, lParam);//意味着开始下一次消息循环
}
return DefWindowProc(hWnd, code, wParam, lParam);//意味着开始下一次消息循环
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hParentInstance, LPSTR lpCmd, int len) {
g_hInstance = hInstance;
//1.写主函数
//2.在主函数中注册窗口类 WNDCLASS WNDCLASSEX(64位扩展的)
WNDCLASSEX wc;//wc是一个窗口类 类型的 结构体,它里面有很多成员变量,一个一个给它们初始化就可以了
wc.cbSize = sizeof(WNDCLASSEX);//窗口类大小
wc.cbClsExtra = NULL;//窗口类附加信息 即 对窗口类的说明 等于NULL表示没有附加信息
wc.cbWndExtra = NULL;//窗口附加信息 即对窗口的说明
wc.lpszClassName = "第一个窗口";//窗口类名,主要是为了标志版权 用spy++工具可以查看类名
//wc.hbrBackground=NULL;//背景 设置背景颜色
wc.hbrBackground = (HBRUSH)GetStockObject(3);//百度查,记不住
wc.hCursor = LoadCursor(hInstance,MAKEINTRESOURCE(IDC_CURSOR1));//光标 用loadCursor函数加载光标:loadCursor会加载一个光标资源,并且返回这个光标资源的句柄。
//也可以用(HCURSOR)LoadImage()强制转化返回类型即可
//wc.hIcon = NULL;//窗口的图标(任务栏) 用loadicon函数来加载图标:加载一个图标资源,并且返回这个图标资源的句柄。
wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON2));
//两种方式loadIcon和(HICON)LoadImage(hInstance,MAKEINTRESOURCE(IDI_ICON2),IMAGE_ICON,32,32,LR_LOADTANSPARENT);
//加载方式:LR_LOADTANSPARENT资源方式加载,文件方式加载LOADFROMFILE
wc.hIconSm = NULL;//小图标
wc.hInstance = hInstance;//当前应用程序的实例句柄
wc.lpfnWndProc = WndProc; //消息处理函数=你自己定义的消息处理函数的名字
wc.lpszMenuName = NULL; //菜单
wc.style = CS_HREDRAW | CS_VREDRAW;//窗口风格=水平/垂直滚动条
RegisterClassEx(&wc); //正式注册窗口类,只要把结构体wc的地址传入即可。
//3.创建窗口 CreateWindow //HWND 窗口句柄类型 Handle Window
HWND hWnd = CreateWindowEx( //因为创建窗口函数会返回创建的窗口的句柄,所以首先定义好窗口句柄类型的变量。
NULL,//窗口的附加风格
wc.lpszClassName, //窗口类名
"我的第一个窗口",//窗口名
WS_OVERLAPPEDWINDOW,//窗口的风格
300, 500,//窗口出现的位置
600, 600,//窗口的宽高
NULL,//父窗口实例句柄
NULL,//窗口的菜单的句柄
hInstance,//窗口的实例句柄(当前应用程序实例句柄
NULL);//附加信息 消息是个结构体,消息有两个附加信息:lParam wParam
/*如果创建窗口失败,则CreateWindow的返回值为NULL即0;
*如果创建窗口成功,则CreateWindow的返回值为所创建窗口的句柄
*/
/*
if(NULL==hWnd){
MessageBox(NULL,"创建窗口失败","警告",MB_OK);
return -1;
}
else{
MessageBox(NULL,"创建窗口成功","恭喜",MB_OK);
}
*/
//4.显示刷新窗口 ShowWindow updateWindow
ShowWindow(hWnd, 5);//传入窗口的句柄即可。
UpdateWindow(hWnd); //传入窗口的句柄即可。
AllocConsole();//得到命令行窗口使用权限
g_hConsole = GetStdHandle(STD_OUTPUT_HANDLE);//用函数得到命令行窗口句柄,赋值给全局变量g_hConsole
//5.定义窗口的消息处理函数
//消息循环是一个死循环,永远不会结束,除非。。。你写一个语句break
while (1) {
//获取消息
//GetMessage(&msg, NULL, NULL, NULL);//GetMessage函数是一个阻塞函数,如果接收一个消息所用的时间过长,在此期间内,如果有另一个消息被发送,就会接受不到这个消息
if(PeekMessage(&msg, NULL, NULL,NULL,NULL))//最后一个参数表示是否删除消息
/*PeekMessage的工作原理:非阻塞方式接受消息,主要查看是否有消息,不会将消息存入MSG结构体中,GetMessage函数负责将消息存入MSG结构体中。
Peekmessage会检查消息队列,如果消息队列中有消息,函数就会查看最后一个参数,
如果最后一个参数为false或者NULL,直接返回1
如果最后一个参数为true,从消息队列中删除消息,然后返回1
如果消息队列中没有消息,直接返回0
*/{
if(0 == GetMessage(&msg, NULL, NULL, NULL)) break;
/*GetMessage工作原理:
GetMessage从消息队列中获取一个消息存放到MSG结构体中。
如果消息队列中有消息,把消息存入MSG,删除当前程序消息队列中的消息,返回非0
直到遇到WM_QUIT消息才返回0.注意:关闭窗口不会发送WM_QUIT消息,只会发送WM_DESTROY消息,只有在消息处理函数中定义当收到WM_DESTROY时,向消息队列中添加WM_QUIT消息,
才能通过if(0 == GetMessage(&msg, NULL, NULL, NULL)) break;退出循环。
如果当前程序消息队列中没有消息,就去系统消息队列中看有没有消息。如果系统消息队列
里面有消息,就把系统消息队列中的消息放入当前程序消息队列。如果系统消息队列中没有消息,就
检查窗口是否需要重新绘制。如果需要重新绘制,操作系统发送WM_PAINT消息,如果不需要重新绘制
查看是否有定时器,如果有定时器,处理定时器消息,如果没有定时器,优化资源,处理内存,继续等待。
消息循环处理消息的次数可能小于消息处理函数处理消息的次数,因为其他应用程序的消息也可能发送给消息处理函数处理。
*/
/*
if (msg.message == WM_QUIT) { //WM_QUITE消息表示整个窗口退出
break;
}
*/
//翻译消息
TranslateMessage(&msg);//翻译消息
/*消息的翻译主要针对键盘消息,键盘消息只有两个:
WM_TDOWN
WM_TUP
if(msg.message==WM_KEY 按键消息){
if(是否可见字符){//可见字符即代表字符有回显,例如F1F2..PgUp等不是可见字符。
//如果是可见字符,代表应该告诉应用程序,用户按键是用来编辑,发送的是一个字符,否则,就认为用户按键是发送一个指令。
//如果用户发送的是一个字符,就需要翻译一下
if(Caps Lock是否按下){
PostMessage(大写字母消息);//发送大写字母消息
}else{
PostMessage(小写字母消息);//发送小写字母消息
}
}
}
return;
*/
DispatchMessage(&msg);//派发消息,群发消息,给当前所有的应用程序都发送。而PostMessage表示只对当前应用程序的消息处理函数发送消息。
}
}
//6.消息循环 循环 接受消息 翻译消息 派发消息
//点击动作 操作系统产生消息 发给窗口应用程序,应用程序里面的“消息循环”接收消息,调用对应的消息处理函数,产生对应的响应动作。
return 0;
}
在注册窗口类的时候设置光标
改进版光标变化程序
#include <windows.h>
#include <cstdio>
template <typename T>
void MyPrintf(char* str,T data){
AllocConsole();
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE);
HANDLE hError = GetStdHandle(STD_ERROR_HANDLE);
char buff[1024]={0};
sprintf(buff,str,data);
WriteConsole(hOutput,buff,strlen(buff),NULL,NULL);
return;
}
HINSTANCE g_hInstance;
void OnCreate(){
//加载光标资源
//g_hCursorRight = LoadImage(g_hInstance,MAKEINTRESOURCE(IDC_RIGHT),IMAGE_CURSOR,32,32,LR_LOADTRANSPARENT)
return;
}
void OnMousemove(HWND hwnd,LPARAM lParam){
//判断当前鼠标在哪里
RECT rect;
GetWindowRect(hwnd,&rect);
int width = rect.right - rect.left;
//MyPrintf("width:%d\n",width);
int middleX = rect.left + width/2;
//MyPrintf("middleX:%d\n",middleX);
//MyPrintf("left:%d ",rect.left);
//MyPrintf("right:%d\n",rect.right);
int x = LOWORD(lParam);
MyPrintf("鼠标当前的x:%d\n",x);
//根据位置设置光标
if(x > middleX){
//SetClassLong(hwnd,GCL_HCURSOR,(LONG)g_hCursorRight);
MyPrintf("%s\n","鼠标在右边");
}else{
MyPrintf("%s\n","鼠标在左边");
}
return;
}