前面我们介绍了win32程序框架,并写了一个程序,而这个程序用的就是消息机制。消息机制作为win32程序开发中重要的部分,下面将通过一些案例来认识消息机制。
消息机制
是什么消息机制,顾名思义,从字面意思上来理解,就是对消息的处理方式。我们在使用手机的时候,打开一个软件,我们在点击某一个选项的时候,系统就会获取到你点击的信息,然后传送到处理中心,当然,仅仅只是获取你息还不能够完成操作,还需要消息的传送,然后做出与之对应的操作。这也就是
为什么我们点击一个选项的时候,不会出现错误打开的情况。当然,这只是简单的分析,手机的运行不仅仅是如此的简单。
windows消息机制:产生消息,传递消息,处理消息;
①.GetMessage(); 只要不是退出消息,返回值就为true;
②.PeekMessage();只要是有消息参数,就返回true,否则返回false;
代码示例:
-- GetMessage()函数;
//参数设置:
BOOL GetMessage(
LPMSG 【LPMSG】, //具有消息结构的地址
HWND 【的hWnd】, //窗口的句柄
UINT 【wMsgFilterMin】, //第一个消息
UINT 【wMsgFilterMax】 // last message
);
while (GetMessage(&msg, nullptr, 0, 0)) //当获得的消息为真时,就返回true;
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) //判断是不是快捷键;
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
-- PeekMessage()
//参数设置
BOOL PeekMessage(
LPMSG 【LPMSG】, //指向消息结构的指针
HWND 【的hWnd】, //处理窗口
UINT 【wMsgFilterMin】, //第一个消息
UINT 【wMsgFilterMax】, // last message
UINT 【wRemoveMsg】 //删除标志
);
MSG msg; //使用peekMessage()
ZeroMemory(&msg, sizeof(msg)); //初始化;
/*
VOID ZeroMemory(
PVOID 【目的地】, //填写零的块地址
DWORD 【长度】 //以块的大小(以字节为单位)填充零
);
*/
while (msg.message != WM_QUIT) //如果不是退出的消息,就执行消息循环;
{
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) //只要鼠标移动,就执行; PM_NOEMOVE是不移动;
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) //判断是不是快捷键;
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
可能有很多人很疑惑,同样是获取消息,那么为什么两个函数的使用方法会不一样,这个就是由于他们的特性决定的,GetMessage()函数,只要得到的不是退出的消息,就会返回true,而PeekMessage()函数只要有消息参数就返回true。如果在while中直接使用PeekMessage)函数,那么窗口就会直接闪退,你来不及操作,系统会认为你没有给他消息,于是就返回了false,也就会闪退;
由于他们的特性决定,在软件开发中一般使用的是GetMessage(),而在游戏开发中就是用的是PekMessage();
SendMessage()
:该函数的消息传送是插队的,也就是说系统会将现在获取的消息优先执行;
PostMessage()
:该函数的消息传送是排队的,也就是按照消息产生的顺序传送,依次执行。
用一张图来解释:
代码演示:
-- SendMessage()
//参数设置
LRESULT SendMessage(
HWND 【的hWnd】, //目标窗口的句柄
UINT 【消息】, //要发送的消息
WPARAM 【wParam中】, //第一个消息参数
LPARAM 【lParam的】 //第二个消息参数
);
HWND hwnd = FindWindowA(0, "我的Android手机"); //查找窗口;
for (i = 0; i < 10; i++)
{
SendMessage(hwnd, WM_PASTE, 0, 0);
SendMessage(hwnd, WM_KEYDOWN, VK_RETURN, 0);
}
-- PostMessage():
//参数设置
BOOL PostMessage(
HWND 【的hWnd】, //目标窗口的句柄
UINT 【消息】, //要发帖
WPARAM 【wParam中】, //第一个消息参数
LPARAM 【lParam的】 //第二个消息参数
);
HWND hwnd = FindWindowA(0, "我的Android手机"); //查找窗口;
for (i = 0; i < 10; i++)
{
PostMessage(hwnd, WM_PASTE, 0, 0);
PostMessage(hwnd, WM_KEYDOWN, VK_RETURN, 0);
}
我们在获取消息后,根据获得的消息就可以实现自己的需求,比如我们在获得某一个消息的时候,与之对应的操作就会产生。比如产生一个弹窗,或者出现一句话。等等。
比如这里的操作就是我们在点击某一个选项后出现的。
case ID_32774:
{
int i = 0;
HWND hwnd = FindWindowA(0, "我的Android手机"); //查找窗口;
for (i = 0; i < 10; i++)
{
PostMessage(hwnd, WM_PASTE, 0, 0);
PostMessage(hwnd, WM_KEYDOWN, VK_RETURN, 0);
}
MessageBox(hwnd,_T("消息轰炸开始"),_T("提示"),0); //消息处理
}
WM_CREATE()
窗口创建前响应。
case WM_CREATE:
MessageBoxA(hWnd,"你是否要打开窗口"," 提示",0); //利用弹窗来显示窗口创建成功;
break;
WM_DESTROY
case WM_DESTROY:
MessageBoxA(hWnd, "你将会关闭窗口", "警告", MB_OKCANCEL); //弹窗显示;
PostQuitMessage(0);
break;
WM_ACTIVATE
①.WA_ACTIVE 通过鼠标单击以外的某些方法激活(例如,通过调用SetActiveWindow功能或使用键盘界面选择窗口)。
②.WA_CLICKACTIVE 通过鼠标点击激活。
③.WA_INACTIVE 停用。
代码示例:
case WM_ACTIVATE: //窗口激活;
{
switch (wParam)
{
case WA_CLICKACTIVE: //鼠标点击激活;
{
HDC hdc = GetDC(hWnd);
static int j = 0;
TextOutA(hdc, 0, j, "加油,未来可期", 7);
j += 20;
}
break;
case WA_ACTIVE: //通过鼠标以外的方式激活;
{
HDC hdc = GetDC(hWnd);
static int j = 0;
TextOutA(hdc, 30, j, "加油,未来可期", 7);
j += 20;
}
break;
case WA_INACTIVE: //停用;
{
MessageBoxA(hWnd, "停用", "提示", 0);
}
break;
}
}
代码的示例就是这样,win32中的消息很多,只能列举一些,这些我们不需要去死记硬背,不会的可以去查表。win32手册参考表;
windows为按键的一个真实键值准备了一个虚拟键值,对于ascii码中有的数据,用ASCII码就可以表示,如果没有,则为VK_·····;
我们在给软件设置快捷键的时候就会用到这里,但是一般不会用这里,一般在创建菜单的时候就会写。下一节我们讲.
这里是写了一个关于回车键的字符消息,一般来说,我们在写快捷键的时候,如果是ALT加某一个字符,是不需要写alt的。
case WM_CHAR: //字符消息;
{
switch (wParam)
{
case VK_RETURN:
MessageBoxA(hWnd, "回车键", "提示", 0);
break;
case 'Z':
{
HDC hdc = GetDC(hWnd);
TextOutA(hdc, 30, 30, "快捷键Alt+X", 8);
ReleaseDC(hWnd, hdc);
}
break;
}
}
// WindowsProject2.cpp : 定义应用程序的入口点。
//
/*
1.入口函数;
2.窗口注册函数
3.窗口创建函数
4.显示窗口
5.更新窗口
6.主消息循环
7.入口函数返回;
*/
#include "framework.h"
#include "WindowsProject2.h"
#define MAX_LOADSTRING 100
// 全局变量:
HINSTANCE hInst; // 当前实例
WCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名
// 此代码模块中包含的函数的前向声明:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
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_WINDOWSPROJECT2, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// 执行应用程序初始化:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WINDOWSPROJECT2));
// MSG msg;
/* //窗口示例,如果需要就可以去掉;
if (MessageBox(0, _T("你是否要选择轰炸"), _T("提示"), MB_ABORTRETRYIGNORE) == IDABORT)
{
MessageBoxW(0, _T("对你要轰炸的对象操作"), _T("选择按钮"), 0);
}*/
//自己添加的消息提示
// 主消息循环:
//while (GetMessage(&msg, nullptr, 0, 0)) //当获得的消息为真时,就返回true;
//{
// if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) //判断是不是快捷键;
// {
// TranslateMessage(&msg);
// DispatchMessage(&msg);
// }
//}
MSG msg; //使用peekMessage()
ZeroMemory(&msg, sizeof(msg)); //初始化;
/*
VOID ZeroMemory(
PVOID 【目的地】, //填写零的块地址
DWORD 【长度】 //以块的大小(以字节为单位)填充零
);
*/
while (msg.message != WM_QUIT) //如果不是退出的消息,就执行消息循环;
{
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) //只要鼠标移动,就执行; PM_NOEMOVE是不移动;
{
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; //风格;
wcex.lpfnWndProc = WndProc; //**非常重要(窗口消息处理函数)
wcex.cbClsExtra = 0; //类的额外信息;
wcex.cbWndExtra = 0; //窗口的额外信息;
wcex.hInstance = hInstance; //当前实例句柄;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOWSPROJECT2)); //图标,菜单栏图标;
wcex.hCursor = LoadCursor(0, nullptr); //光标;
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); // 背景,一般不需要改变;
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WINDOWSPROJECT2); // 菜单;
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| WS_VSCROLL, // 窗口风格;
CW_USEDEFAULT, //窗口的坐标x
0, // 窗口的y坐标
CW_USEDEFAULT, //窗口的宽度
0, //窗口的高度
nullptr, //父窗口菜单句柄
nullptr, // 窗口菜单句柄
hInstance, //当前实例句柄
nullptr); // 保留参数;
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// 函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 目标: 处理主窗口的消息。
//
// WM_COMMAND - 处理应用程序菜单
// WM_PAINT - 绘制主窗口
// WM_DESTROY - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CHAR: //字符消息;
{
switch (wParam)
{
case VK_RETURN:
MessageBoxA(hWnd, "回车键", "提示", 0);
break;
case 'Z':
{
HDC hdc = GetDC(hWnd);
TextOutA(hdc, 30, 30, "快捷键Alt+X", 8);
ReleaseDC(hWnd, hdc);
}
break;
}
}
//case WM_ACTIVATE: //窗口激活;
//{
// switch (wParam)
// {
// case WA_CLICKACTIVE: //鼠标点击激活;
// {
// HDC hdc = GetDC(hWnd);
// static int j = 0;
// TextOutA(hdc, 0, j, "加油,未来可期", 7);
// j += 20;
// }
// break;
//case WA_ACTIVE: //通过鼠标以外的方式激活;
//{
// HDC hdc = GetDC(hWnd);
// static int j = 0;
// TextOutA(hdc, 30, j, "加油,未来可期", 7);
// j += 20;
//}
// break;
//case WA_INACTIVE: //停用;
//{
// MessageBoxA(hWnd, "停用", "提示", 0);
//}
//break;
/* }
}*/
case WM_CREATE: //窗口创建;
MessageBoxA(hWnd,"你是否要打开窗口"," 提示",0); //利用弹窗来显示窗口创建成功;
break;
case WM_COMMAND: //菜单命令;
{
int wmId = LOWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
break;
case ID_32774:
{
int i = 0;
HWND hwnd = FindWindowA(0, "我的Android手机"); //查找窗口;
for (i = 0; i < 10; i++)
{
PostMessage(hwnd, WM_PASTE, 0, 0);
PostMessage(hwnd, WM_KEYDOWN, VK_RETURN, 0);
}
MessageBox(hwnd,_T("消息轰炸开始"),_T("提示"),0);
}
case IDB_BITMAP1:
MessageBox(0, 0, 0, 0);
break;
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);
// TODO: 在此处添加使用 hdc 的任何绘图代码...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
MessageBoxA(hWnd, "你将会关闭窗口", "警告", MB_OKCANCEL); //弹窗显示;
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;
}
win32中的消息机制是实现操作的重要的部分,但是软件开发不仅仅是这么的简单,设置一个窗口还需要学到菜单等知识,下一节会介绍。这一节就是介绍了一些常用的消息,主要的是学会查表。并能够实际操作。如果有兴趣的同学可以去我主页的资源中,里面有一个用窗口实现的表白资源包
;