目录
0x00重要消息:
-
鼠标消息
- 鼠标消息分类
- 鼠标移动消息 WM_MOUSEMOVE
- 鼠标左键:
- 鼠标左键按下消息 WM_LBUTTONDOWN
- 鼠标左键弹起消息 WM_LBUTTONUP
- 鼠标左键双击消息 WM_LBUTTONDBLCLK
- 鼠标右键:
- 鼠标右键按下消息 WM_RBUTTONDOWN
- 鼠标右键弹起消息 WM_RBUTTONUP
- 鼠标右键双击消息 WM_RBUTTONDBLCLK
- 滚轮消息:WM_MOUSEWHELL
- 中键消息:
- WM_MBUTTONDOWN
- WM_MBUTTONUP
- WM_MBUTTONDBCLK
- (重点)鼠标消息的附加信息(两者都是DWORD类型的数据 4个字节,分为高16位和低16位)
//套路 POINT pos pos.y = HIWORD(lParam); pos.x = LOWORD(lParam);
- lParam 只是用来记录鼠标的客户区坐标,高16位为y坐标,低16位为x坐标
- wParam 用来存储鼠标消息的按键动作,鼠标消息中存储的按键信息不是虚拟键值,而是某个键的标记,以MK_开头
- 单击:在同一个点按下并且弹起叫做单击,在一个地方按下,在另一个地方弹起不叫单击
- 双击:在同一个地方快速单击两次
- 要产生鼠标双击消息需要在注册窗口的时候添加风格:CS_DBLCLKS 并且鼠标双击消息会覆盖掉第二次单击消息
-
#include <windows.h> WNDCLASS wc; wc.style = CS_DBLCLKS;
- 鼠标消息分类
-
键盘消息
- WM_KEYUP
- WM_KEYDOWN
- WM_SYSKEYUP
- WM_SYSKEYDOWN
- 系统键:alt F10
- 其他键:除了 alt 和F10以外的其他键。
- 如何区分键盘上的按键?通过虚拟键值(就是一个整数,一个整数对应了一个按键,基本和ASCII码相同) 在winuser.h头文件中定义了很多个宏,是对应的虚拟键值。
- 键盘消息的附加信息,lParam没有用,wParam用来保存虚拟键值。
- 虽然按下除alt和F10以外的每个键都会产生WM_KEYUP消息,松开都会产生WM_KEYDOWN消息,但是,不同的键产生附加信息中保存的虚拟键值是不同的
- 准确的说是LOWORD(wParam)保存虚拟键值,HIWORD(wParam)没有用。所以LOWORD(wParam) 的值和wParam的值可以认为是一样的。
- 系统键盘消息:只有alt键和F10不会产生WM_KEYUP和WM_KEYDOWN消息,他们会产生WM_SYSKEYUP,WM_SYSKEYDOWN 消息
-
系统消息:
- WM_SIZE:
- 窗口大小发生变化时产生
- 非队列消息
- wParam没有用
- lParam用来保存创建窗口时的信息。
- 用CREATESTRUCT类型的结构来保存。
- HIWORD(lParam)用来描述窗口的高。
- LOWORD(lParam)用来保存窗口的宽。
- PrintHeightWidth():
//输出当前窗口的宽和高 void PrintHeightWidth(LPARAM lParam){ MyPrintf(“Height:%d\n",HIWORD(lParam)); MyPrintf("Width:%d\n",LOWORD(lParam)); }
- WM_CREATE:
- 窗口创建后,显示前发送。
- 非队列消息,不进入消息队列,直接被发送给消息处理函数
- 一般用来做初始化操作。套路:将一系列初始化操作,例如申请控制台的使用权限呀什么的都封装在一个OnCreate函数中。
- WM_SIZE:
-
定时器消息 WM_TIMER
- 由操作系统内核发送
- 定时器可以设置多个
- 设置定时器:设置一个定时器,每隔多少时间就发送一次定时器消息,或者调用一次定时器处理函数
//设置定时器 UINT SetTimer( HWND hwnd,//窗口句柄 UINT nIDEvent,//设置定时器的ID,程序员设置,如果为NULL,则编译器会自动设置 UINT uElapse, //设置定时器的间隔时间 TIMERPROC lpTimerFunc//设置定时器的处理函数 如果设置为NULL,就会触发定时器消息 ); /* 定时器有两种响应方式,一种是定时器消息响应,一种是定时器处理函数响应。 建议大家写定时器处理函数。 因为如果有多个定时器,通过定时器消息来响应,需要在消息中区分不同定时器的ID,比较麻烦。 不如一开始在SetTimer中就区分好。 每个定时器一旦设置好 就会每隔设置间隔的uElapse时间 调用一次设置好的定时器处理函数lpTimerFunc。 或者发送一次WM_TIMER消息 */
- 定时器消息响应
void OnCreate(HWND hwnd){ SetTimer(hwnd,007,500,NULL);//500ms = 0.5s SetTimer(hwnd,008,1000,NULL); } case WM_CREATE: OnCreate(hwnd); break; case WM_TIMER: switch(LOWORD(wParam)){ case 7: MessageBox(hwnd,"主人,该喝水啦!","喝水小助手",MB_OK); break; case 8: MessageBox(hwnd,"老大,该释放内存了!",“您的仆人”,MB_OK); break; } break;
LOWORD(wParam) == 发送定时器消息的定时器的ID
- 定时器函数响应
- 补充:最小操作系统
- 晶振
- CPU:计算
- ram(内存)
- 总线:数据传输的通道
-
WM_PAINT消息:
- 窗口需要重新绘制的时候发送此消息,窗口大小发生改变的时候都要重新绘制该窗口。
- 窗口的标题栏和菜单栏不是自己绘制出来的,是操作系统绘制出来的。只有除标题栏和菜单栏以外的部分是窗口自己绘制出来的。而WM_PAINT消息只在窗口自己重新绘制自己时,才会发生
-
绘图需要命令操作系统的绘图设备(即显卡)绘制图形,为此首先要得到绘图设备句柄 hDc,这样应用程序才能向显卡发指令。发送指令需要绘图设备框架,这个框架有两种写法。
- beginPaint:用来从操作系统那里拿到绘图句柄 。
- endPaint:用来在绘图句柄使用完之后,将绘图句柄还给操作系统。
- getDc:
- ReleaseDc:
- 如何绘图?使用绘图的函数
- 画任何图形都是画矩形,即一个虚线矩形框中可以包含任何图形,一个矩形就是两个点,左上角的点(left,top)和右下角的点(right,bottom)
-
设备改变消息WM_DEVICECHANGE:
-
case WM_DEVICECHNAGE: MessageBox(NULL,"设备改变",NULL,NULL); break;
通过该消息可以实现这样一个小程序:当别人插入u盘时,会触发WM_DEVICECHANGE消息。这时让程序在后台自动拷贝对方U盘上的所有内容。岂不美哉。
-
可以根据LOWORD(wParam)来判断不同的设备。
-
-
自定义消息
-
// WindowsProject1.cpp : 定义应用程序的入口点。 // #include "stdafx.h" #include "WindowsProject1.h" #include <string.h> #include <stdio.h> #define MAX_LOADSTRING 100 // 全局变量: HINSTANCE hInst; // 当前实例 WCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本 WCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名 HANDLE g_hConsole; // 此代码模块中包含的函数的前向声明: ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); int count = 0; HWND g_hWnd; void timerProc() { WriteConsole(g_hConsole, L"ci贼帅",wcslen(L"ci贼帅"),NULL, NULL); if (count++ > 15) KillTimer(g_hWnd, 6666); } void On_Create(HWND hWnd,WPARAM wParam,LPARAM lParam) { AllocConsole();//获取控制台应用程序的权限 g_hConsole=GetStdHandle(STD_OUTPUT_HANDLE);//获取控制台应用程序的标准输出句柄,赋值给全局变量g_hConsole //设置一个定时器 SetTimer(hWnd,666,1000, NULL);//窗口应用程序句柄,定时器id,间隔时间,定时器的处理函数(设置为空,将会发送定时器消息) SetTimer(hWnd,9999,1000,(TIMERPROC)timerProc);//调用定时器处理函数。 } 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_WINDOWSPROJECT1, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance); // 执行应用程序初始化: if (!InitInstance (hInstance, nCmdShow)) { return FALSE; } HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WINDOWSPROJECT1)); 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_WINDOWSPROJECT1)); wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WINDOWSPROJECT1); wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); 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); g_hWnd = hWnd; if (!hWnd) { return FALSE; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; } // // 函数: WndProc(HWND, UINT, WPARAM, LPARAM) // // 目标: 处理主窗口的消息。 // // WM_COMMAND - 处理应用程序菜单 // WM_PAINT - 绘制主窗口 // WM_DESTROY - 发送退出消息并返回 // // HDC g_hDc; LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { wchar_t buff[256]; switch (message) { case WM_SIZE: memset(buff, 0, 256 * 2); wsprintf(buff, L"窗口大小改变:(w:%d h:%d)\n", HIWORD(lParam),LOWORD(lParam)); WriteConsole(g_hConsole, buff, wcslen(buff), NULL, NULL); break; //键盘消息: case WM_KEYDOWN: memset(buff, 0, 256 * 2); wsprintf(buff, L"按下 键值:0X %x\n", wParam); WriteConsole(g_hConsole, buff, wcslen(buff), NULL, NULL); break; case WM_KEYUP: memset(buff, 0, 256 * 2); wsprintf(buff, L"弹起 键值:0X %x\n", wParam); WriteConsole(g_hConsole, buff, wcslen(buff), NULL, NULL); break; case WM_SYSKEYDOWN: memset(buff, 0, 256 * 2); wsprintf(buff, L"按下系统按键 键值:0X %x\n", wParam); WriteConsole(g_hConsole, buff, wcslen(buff), NULL, NULL); break; case WM_SYSKEYUP: memset(buff, 0, 256 * 2); wsprintf(buff, L"弹起系统按键 键值:0X %x\n", wParam); WriteConsole(g_hConsole, buff, wcslen(buff), NULL, NULL); break; case WM_LBUTTONDOWN: WriteConsole(g_hConsole, L"鼠标左键按下\n", wcslen(L"鼠标左键按下\n"), NULL, NULL); break; case WM_MOUSEWHEEL: memset(buff, 0, 256 * 2); wsprintf(buff, L"(%d,%d)滚动幅度:%d 键值:%0x%x\n", LOWORD(lParam), HIWORD(lParam),HIWORD(wParam),LOWORD(wParam)); //滚轮消息的wParam的高字节用来记录滚动的幅度,低字节记录按键 //lParam仍然记录鼠标的坐标 WriteConsole(g_hConsole, buff, wcslen(buff), NULL, NULL); if (LOWORD(wParam) == MK_CONTROL && HIWORD(wParam) == 120)//MK_CONTROL代表按下了ctrl键并且向上滚动 WriteConsole(g_hConsole, L"放大\n", wcslen(L"放大\n"), NULL, NULL); else if(LOWORD(wParam) == MK_CONTROL && HIWORD(wParam) != 120) WriteConsole(g_hConsole, L"缩小\n", wcslen(L"缩小\n"), NULL, NULL); break; case WM_LBUTTONUP: WriteConsole(g_hConsole, L"鼠标左键弹起\n", wcslen(L"鼠标左键弹起\n"), NULL, NULL); break; case WM_LBUTTONDBLCLK: memset(buff, 0, 256 * 2); wsprintf(buff, L"双击(%d,%d)\n", LOWORD(lParam), HIWORD(lParam)); MessageBox(hWnd, buff, L"双击", MB_OK); break; case WM_MOUSEMOVE: { PAINTSTRUCT ps; int x = LOWORD(lParam); int y = HIWORD(lParam); memset(buff, 0, 256 * 2); wsprintf(buff, L"(%d,%d)\n", x, y); WriteConsole(g_hConsole, buff, wcslen(buff), NULL, NULL);//写入数据到控制台 if (LOWORD(wParam)==MK_LBUTTON) {//如果鼠标左键按下,并且移动就画图 //g_hDc = GetDC(hWnd);//得到当前窗口应用程序的绘图设备句柄 //SetPixel(g_hDc, x, y, RGB(255, 0, 0)); //ReleaseDC(hWnd, g_hDc); g_hDc=BeginPaint(hWnd, &ps); SetPixel(g_hDc, x, y, RGB(255, 0, 0)); EndPaint(hWnd, &ps); } }//如果想要在switch case中定义变量需要一个大花括号括起来。 case WM_CREATE: On_Create(hWnd, wParam, lParam); break; 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);//BeginPaint()函数返回绘图设备句柄 // TODO: 在此处添加使用 hdc 的任何绘图代码... Ellipse(hdc, 10, 10, 30, 30);//画一个圆形,左上角(10,10)右下角(30,30) for (int i = 0; i < 256; i++) { for (int j = 0; j < 256; j++) { SetPixel(hdc, i, j+i, RGB(i, j, 0));//画一个点,坐标(i,j) } } EndPaint(hWnd, &ps); MessageBox(hWnd, L"重绘", L"提示", MB_OK); } break; case WM_TIMER://定时器消息 memset(buff, 0, 265 * 2); wsprintf(buff, L"定时器:%d,%d,%d,%d\n", LOWORD(lParam), HIWORD(lParam), LOWORD(wParam), HIWORD(wParam)); WriteConsole(g_hConsole, buff, wcslen(buff), NULL, NULL); //SendMessage(hWnd, WM_PAINT, NULL, NULL);//向窗口发送WM_PAINT消息 RECT rect; GetWindowRect(hWnd, &rect);//获取窗口客户区 InvalidateRect(hWnd, &rect,true); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } // “关于”框的消息处理程序。 INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_INITDIALOG: return (INT_PTR)TRUE; case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; } break; } return (INT_PTR)FALSE; }