IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> C++ win32编程 02 常见消息 -> 正文阅读

[游戏开发]C++ win32编程 02 常见消息

02 常见消息

1 打印消息相关信息

1.1 将消息内容转化为字符串

第一步: 定义字符串变量,用来保存转化后的消息

wchar_t szInfo[300];  //定义消息内容变量

第二步:用宽字符格式化函数转化消息内容\

wsprintf(szInfo, "hWnd=%d\tuMsg=%d\twParam=%d, lParam=%d", 
         		  hWnd, 	uMsg, 	wParam, 	lParam);

这里说明一下,为什么四个参数都可以直接以16进制形式进行格式化

参数类型本质
hWndHWND结构体指针
uMsgUINTunsigned int 无符号整型
wParamWPARAMtypedef UINT_PTR WPARAM;
lParamLPARAMtypedef LONG_PTR LPARAM;

不同的消息,wParam 与 lParam会有不同的意义

2 窗体创建消息

消息名: WM_CREATE

触发对象: 当调用CreateWindow 或 CreateWindowEx创建窗体完后触发

触发时机: 调用以上两个函数完毕后立即触发,此时窗体还未显示

附加参数意义:

wParam: 没用到

lParam: 一个指向 CREATESTRUCT 结构体的指针,类型为LPCREATESTRUCT

注意: 按照一般命名规则,类型名前加LP表示指针类型

CREATESTRUCT 结构体成员

typedef struct tagCREATESTRUCT {
  LPVOID    lpCreateParams;		//指向将被用于创建窗口的数据的指针
  HINSTANCE hInstance;			//窗体所属应用程序句柄
  HMENU     hMenu;				//窗体菜单句柄
  HWND      hwndParent;			//窗体父窗体句柄
  int       cy;					//窗体高度
  int       cx;					//窗体宽度
  int       y;					//窗体左上角y坐标
  int       x;					//窗体左上角x坐标
  LONG      style;				//窗体显示风格
  LPCTSTR   lpszName;			//指向以结束符('\0')表示结尾的字符串,指定了新窗口的名字。
  LPCTSTR   lpszClass;			//指向以结束符('\0')表示结尾的字符串,指定了新窗口的类名
  DWORD     dwExStyle;			//窗体的扩展样式
} CREATESTRUCT, *LPCREATESTRUCT;

本例测试代码:

case WM_CREATE:
	{

		wchar_t szcsInfo[300];
		LPCREATESTRUCT	lpcs = (LPCREATESTRUCT)lParam;
		wsprintf(szcsInfo, L"窗体类名: %s, 窗体名称: %s\n", 
                 			lpcs->lpszClass, lpcs->lpszName);
		OutputDebugString(szcsInfo);
		break;
	}

运行效果截图:

img

注意:

lParam是一个结构体指针,在使用时注意强制转化为相应的类型

LPCREATESTRUCT	lpcs = (LPCREATESTRUCT)lParam;

结构体类型: CREATESTRUCT

相应的结构体类型指针: LPCREATESTRUCT

在看帮助文档的时候,注意看清楚

img

3 窗口关闭消息

消息名: WM_CLOSE

触发对象: 点击窗口右上角的关闭(X)按钮

触发时机:点击后发送

wParam: 没用(0)

lParam: 没用(0)

示例代码:

case WM_CLOSE: //处理关闭按钮消息
{
    int btnValue = MessageBox(hWnd, L"是否确认关闭窗口?", L"温馨提示", MB_YESNO);
    if (btnValue == IDYES){
        DestroyWindow(hWnd); //销毁窗口
        break;
    }
    else{
        return 1;
    }
}

解析:

按下关闭按钮会弹出对话框,确认是否关闭

int btnValue = MessageBox(hWnd, L"是否确认关闭窗口?", L"温馨提示", MB_YESNO);

当弹出对话框按下确认按钮时,将会进入关闭窗口流程

if (btnValue == IDYES){
    DestroyWindow(hWnd); //销毁窗口
    break;
}

关闭主程序与关闭窗口是两个不同的事情

这个我们在上节课已经学过,关闭窗口只需要调用DestroyWindow即可

DestroyWindow(hWnd); //销毁窗口

但是关闭主程序还需要发送退出消息循环的命令

PostQuitMessage(0);

如果点击关闭然后点击取消按钮,即不想关闭,我们需要将关闭窗体的消息从消息对列清楚出去

else{
    return 1;
}

当我们将消息交给系统处理程序的代码写在switch之外的时候,这个地方必须要return 一下,以防止关闭消息被系统处理程序重新处理一边,导致的窗体关闭

即:我们不主动处理窗口关闭事件,只要这个消息流入系统消息处理函数,系统也会帮我们关闭窗口.

但是如果关闭这个窗口后想要进一步关闭主程序,就必须在WM_DESTROY消息中发送退出消息循环的命令

PostQuitMessage(0);来结束程序的进程

如果系统消息处理函数是写在switch的default分支中的,则只需要直接break即可实现不关闭窗口的效果

测试代码如下:

LRESULT CALLBACK fWinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg){
	case WM_CLOSE: //处理关闭按钮消息
	{
		int btnValue = MessageBox(hWnd, L"是否确认关闭窗口?", L"温馨提示", MB_YESNO);
		if (btnValue == IDYES){
			DestroyWindow(hWnd); //销毁窗口
		}
		break;
	}
	case WM_DESTROY:
		PostQuitMessage(0); //发送退出消息
		break;
	default:
		return DefWindowProc(hWnd, uMsg, wParam, lParam);
	}
	return 1;
}				

特别要注意:

(1) 在没有调用DestroyWindow函数前,窗体关闭过程可以中止,一旦调用了DestroyWindow,窗口关闭就无法逆转

因此关闭窗口的关键在于调用DestroyWindow函数的时机

(2) 再强调一下关闭窗口与关闭程序是两个不同的概念,关闭窗口只需要DestroyWindow即可,关闭程序要调用PostQuitMessage函数

4 鼠标消息

4.1 鼠标左键消息

消息名称消息含义
WM_LBUTTONDOWN鼠标左键按下(一个动作,不包含按下的状态)
WM_LBUTTONUP鼠标左键松开(弹起一瞬间的动作)
WM_LBUTTONDBLCLK鼠标左键双击动作(动作完成的一瞬间)
组合键

wParam的可能取值:

含义
MK_CONTROLCtrl键处于按下状态
MK_LBUTTON左键处于按下状态
MK_MBUTTON鼠标中键(滚轮键)处于按下状态
MK_RBUTTON鼠标右键处于按下状态
MK_SHIFTshift键处于按下状态
MK_XBUTTON1
MK_XBUTTON12

判断方法是对可能的取值做与运算,结果为True则按下了,否则没有按下

如判断按下鼠标左键时是否已经按下了CTRL键

case WM_LBUTTONDOWN:
    if (wParam & MK_CONTROL)
    {
    	SetWindowText(hWnd,L"同时按下Ctrl+鼠标左键");
    }
    break;

注意:

用可能的取值去与wParam做&(按位与)运算来判断是否成立

又如: 判断ctrl + shift + 鼠标左键

case WM_LBUTTONDOWN:
    if ((wParam & MK_CONTROL) && (wParam & MK_SHIFT)){
        SetWindowText(hWnd,L"同时按下Ctrl + Shift + 鼠标左键");
    }
    break;

注意:

(1) WM_LBUTTONDWON 消息下MK_LBUTTON没有意义

(2) 在处理了WM_LBUTTONDOWN消息后直接处理WM_LBUTTONDBLCLK 时会导致双击消息无法获得响应

解决方法:

在WM_LBUTTONDOWN对本次的点击时间与上次的点击时间做差,如果差值在指定时间内,则为双击,不在指定时间为则为单击

获取时间的函数:GetMessageTime()函数来获取当次消息发送的时间

函数原型

LONG WINAPI GetMessageTime(void);

返回值是一个表示时间的长整型.这个时间与系统时间无关,从0开始,到超过取值范围后会从0重新开始

因此,如果要判断时间差,需要判断第二个时间>第一个时间

示例代码:

case WM_LBUTTONDOWN:
{
    LONG clickTime = GetMessageTime();
    if (lastClickTime == 0 || clickTime - lastClickTime > 500){
        SetWindowText(hWnd, L"鼠标左键");
    }
    else{
        SetWindowText(hWnd, L"双击鼠标左键");
    }
    lastClickTime = clickTime;
    break;
}

注意: 为简化程序, 本例没有对时间做判断

鼠标的位置信息

lParam中包含了鼠标的位置信息,高位表示y坐标,低位表示x坐标

注意: 坐标是鼠标在窗体客户区的位置 ,不是以屏幕作为参考的坐标位置

有两种方法获取鼠标的x,y坐标

方法一:

int x = GET_X_LPARAM(lParam);
int y = GET_Y_LPARAM(lParam);

注意:此方法要求包含头文件:Windowsx.h

方法二:

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

示例代码:

case WM_LBUTTONDOWN:
{
    //以下显示鼠标位置
    int x2 = LOWORD(lParam);
    int y2 = HIWORD(lParam);
    int x1 = GET_X_LPARAM(lParam);
    int y1 = GET_Y_LPARAM(lParam);
    wchar_t mousePosInfo[100];
    wsprintf(mousePosInfo, L"方法一: 鼠标的x坐标:%d, y坐标:%d\n 方法二: 鼠标的x坐标:%d, y坐标:%d\n",
             x1, y1, x2, y2);
    OutputDebugString(mousePosInfo);
    break;
}

运行结果:

img

4.2 其他鼠标消息

消息名消息含义
WM_RBUTTONDOWN右键按下
WM_RBUTTONUP右键松开(弹起)
WM_RBUTTONDBLCLK右键双击
WM_MBUTTONDOWN中键(滚轮)按下
WM_MBUTTONUP中键(滚轮)松开
WM_MBUTTONDBLCLK中键(滚轮)双击
WM_MOUSEMOVE鼠标移动

附加参数wParam与lParam的意义与用法同左键,不再展开

5 键盘事件

5.1 单个按键按下

消息名称: WM_KEYDOWN

wParam可能的取值, 常用按键表

含义
VK_LBUTTON 0x01鼠标左键按下状态
VK_RBUTTON 0x02鼠标右键按下状态
VK_CANCEL 0x03Ctrl + Break
VK_MBUTTON 0x04鼠标中键
VK_XBUTTON1 0x05X1 mouse button
VK_XBUTTON2 0x06X2 mouse button
VK_BACK 0x08BackSpace
VK_TAB 0x09TAB键
VK_CLEAR 0x0CCLEAR key
VK_RETURN 0x0DENTER key 回车
VK_SHIFT 0x10SHIFT key
VK_CONTROL 0x11CTRL key
VK_MENU 0x12ALT key
VK_PAUSE 0x13PAUSE key
VK_CAPITAL 0x14CAPS LOCK key
VK_KANA 0x15IME Kana mode 别问,问就是不知道
VK_HANGUEL 0x15IME Hanguel mode (maintained for compatibility; use VK_HANGUL) 别问,问就是不知道
VK_HANGUL 0x15IME Hangul mode 别问,问就是不知道
VK_JUNJA 0x17IME Junja mode 别问,问就是不知道
VK_FINAL 0x18IME final mode 别问,问就是不知道
VK_HANJA 0x19IME Hanja mode 别问,问就是不知道
VK_KANJI 0x19IME Kanji mode 别问,问就是不知道
VK_ESCAPE 0x1BESC key
VK_CONVERT 0x1CIME convert 别问,问就是不知道
VK_NONCONVERT 0x1DIME nonconvert 别问,问就是不知道
VK_ACCEPT 0x1EIME accept 别问,问就是不知道
VK_MODECHANGE 0x1FIME mode change request 别问,问就是不知道
VK_SPACE 0x20SPACEBAR 空格键
VK_PRIOR 0x21PAGE UP key
VK_NEXT 0x22PAGE DOWN key
VK_END 0x23END key
VK_HOME 0x24HOME key
VK_LEFT 0x25LEFT ARROW key 方向左
VK_UP 0x26UP ARROW key 方向上
VK_RIGHT 0x27RIGHT ARROW key 方向右
VK_DOWN 0x28DOWN ARROW key 方向下
VK_SELECT 0x29SELECT key
VK_PRINT 0x2APRINT key
VK_EXECUTE 0x2BEXECUTE key
VK_SNAPSHOT 0x2CPRINT SCREEN key
VK_INSERT 0x2DINS key
VK_DELETE 0x2EDEL key
VK_HELP 0x2FHELP key
0x300 key 大键数字0-9
0x311 key
0x322 key
0x333 key
0x344 key
0x355 key
0x366 key
0x377 key
0x388 key
0x399 key
0x41A key 字母A-Z
0x42B key
0x43C key
0x44D key
0x45E key
0x46F key
0x47G key
0x48H key
0x49I key
0x4AJ key
0x4BK key
0x4CL key
0x4DM key
0x4EN key
0x4FO key
0x50P key
0x51Q key
0x52R key
0x53S key
0x54T key
0x55U key
0x56V key
0x57W key
0x58X key
0x59Y key
0x5AZ key
VK_LWIN 0x5BLeft Windows key (Natural keyboard) 左win键
VK_RWIN 0x5CRight Windows key (Natural keyboard) 右win键
VK_APPS 0x5DApplications key (Natural keyboard)
VK_SLEEP 0x5FComputer Sleep key
VK_NUMPAD0 0x60Numeric keypad 0 key 小键数字0-9
VK_NUMPAD1 0x61Numeric keypad 1 key
VK_NUMPAD2 0x62Numeric keypad 2 key
VK_NUMPAD3 0x63Numeric keypad 3 key
VK_NUMPAD4 0x64Numeric keypad 4 key
VK_NUMPAD5 0x65Numeric keypad 5 key
VK_NUMPAD6 0x66Numeric keypad 6 key
VK_NUMPAD7 0x67Numeric keypad 7 key
VK_NUMPAD8 0x68Numeric keypad 8 key
VK_NUMPAD9 0x69Numeric keypad 9 key
VK_MULTIPLY 0x6AMultiply key 小键盘乘号
VK_ADD 0x6BAdd key 小键盘加号
VK_SEPARATOR 0x6CSeparator key 小键盘除号
VK_SUBTRACT 0x6DSubtract key 小键盘减号
VK_DECIMAL 0x6EDecimal key 小键盘.键
VK_F1 0x70F1 key 功能键F1-F24 (为什么我的键盘只到F12的)
VK_F2 0x71F2 key
VK_F3 0x72F3 key
VK_F4 0x73F4 key
VK_F5 0x74F5 key
VK_F6 0x75F6 key
VK_F7 0x76F7 key
VK_F8 0x77F8 key
VK_F9 0x78F9 key
VK_F10 0x79F10 key
VK_F11 0x7AF11 key
VK_F12 0x7BF12 key
VK_F13 0x7CF13 key
VK_F14 0x7DF14 key
VK_F15 0x7EF15 key
VK_F16 0x7FF16 key
VK_F17 0x80F17 key
VK_F18 0x81F18 key
VK_F19 0x82F19 key
VK_F20 0x83F20 key
VK_F21 0x84F21 key
VK_F22 0x85F22 key
VK_F23 0x86F23 key
VK_F24 0x87F24 key
VK_NUMLOCK 0x90NUM LOCK key NumLock键
- 0x97-9FUnassigned
VK_LSHIFT 0xA0Left SHIFT key 左Shift
VK_RSHIFT 0xA1Right SHIFT key 右Shfit
VK_LCONTROL 0xA2Left CONTROL key 左 Ctrl
VK_RCONTROL 0xA3Right CONTROL key 右Ctrl
VK_LMENU 0xA4Left MENU key 左Alt
VK_RMENU 0xA5Right MENU key 右Alt

示例代码

case WM_KEYDOWN:
{
    switch (wParam){
        case 0x30:
            SetWindowText(hWnd, L"0"); //按下了大键盘区的数字0键
            break;
    }
}

注意:

WM_KEYDOWN 消息不能捕获一些特殊的系统按键,如ALT

如果在实际应用中发现没有如期捕获到一些特殊的按键,可能是系统按键,需要用

WM_SYSKEYDOWN消息来捕获这些按键

该事件的附加参数wParam, lParam与WM_KEYDOWN消息的内容是一样的,就不再展开了

示例代码: 捕获ALT键

case WM_SYSKEYDOWN:
        {
            switch (wParam)
            {
                case VK_MENU:
                {
                    OutputDebugString(L"ALT\n");
                    return DefWindowProc(hWnd, uMsg, wParam, lParam);
                }
            }
        }

注意:

使用WM_SYSKEYDOWN 捕获系统按键后要交还给系统处理函数,否则有可能引起一些莫名其妙的错误(这里指我也不知道为什么会有什么后果的错误)

5.2 组合按键检测

当我们需要判断一个按键

用函数GetKeyState 与 GetAsyncKeyState来判断指定按键的状态

两者的区别在于键盘消息生成时的状态与当前调用函数时的按键状态

函数原型:

SHORT WINAPI GetKeyState(
  _In_  int nVirtKey
);

参数: 按键的虚拟值,即上表中的内容

返回值: SHORT

返回值

高位为1时表示按键按下了,其他值为没有按下

低位为1时表示锁定状态,其他值为没有锁定(如CapsLock键,NumsLock键等)

示例代码: Ctrl+F1响应

case VK_F1:                                   //F1键检测
{          
    SHORT bCtrl = GetKeyState(VK_CONTROL);
    if (bCtrl >> 15)
    {
        OutputDebugString(L"CTRL+F1\n");
    }
    break;
}

示例代码: 大小写字母

case 0x41:
{
    SHORT bCapsLock = GetKeyState(VK_CAPITAL);
    if (bCapsLock & 1)
    {
        //大写锁定状态输出A
        OutputDebugString(L"A");
    }
    else
    {
        //没有大写锁定状态输出a
        OutputDebugString(L"a");
    }
    break;
}

Alt+的组合暂时搞不定_

6 字符消息

消息名称: WM_CHAR

消息发送是时机:

(1) 按下字母,数字键

(2) 调用了TranslateMessage函数

wParam: 字符

lParam: 同其他按键附加信息

示例代码: 输出按键字符

case WM_CHAR:
{
    wchar_t charInfo[100];
    wsprintf(charInfo, L"按下的字符:%c", (char)wParam);
    SetWindowText(hWnd, charInfo);
}

img

如果不开启TranslateMessage 则无法生成WM_CHAR消息

7 绘图消息

消息名: WM_PAINT

消息发送时机: 当系统或其他应用程序请求绘制应用程序窗口的一部分时

wParam: 没用

lParam: 没用

示例代码:

case WM_PAINT:
{
    //绘图刷子结构体
    PAINTSTRUCT ps;
    //绘图句柄
    //第一个参数: 需要绘图的窗口句柄, 第二个参数: 绘图刷子结构体 返回值: 绘图句柄
    HDC hDc = BeginPaint(hWnd, &ps); //开始绘图    
    Rectangle(hDc, 0,0, 200, 200); //画一个矩形, 后面四参数依次是:左上x,左上y,宽, 高
    Ellipse(hDc,0,0,200,200);//椭圆(圆可以看成特殊的椭圆)
    EndPaint(hWnd, &ps);  //结束绘图
}

运行效果:

img

这个消息了解一下就好了.

8 其他有用的知识

8.1 在程序运行过程中观察变量动态

第一步:在需要监视变量的地方设置断点

img

第二步: 开启调试

img

第三步: 点击下方监视窗口

img

如果没有监视窗口,可以点菜单栏: 调试->窗口->监视窗口(Ctrl+Alt+W) 然后按1或2或3或4来选择监视窗口

img

第四步:将变量加入监视窗口

(1) 选中断点处的变量名

(2) 按住鼠标不要松开

(3)将变量名拖动到下方监视窗口

![img][10]

8.2 查看其他应用程序的窗口类名与窗口标题

第一步: 打开spy窗口

![img][11]

第二步: 打开偷窥窗口

![img][12]

![img][13]

第三步:点击要查看的应用程序窗口

![img][14]

效果图:

![img][15]

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-04-24 09:46:09  更:2022-04-24 09:48:31 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 14:49:33-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码