我是荔园微风,作为一名在IT界整整25年的老兵,今天来聊聊微软MFC技术中的消息队列及消息处理。
MFC应用程序中由Windows 系统以消息的形式发送给应用程序的窗口。窗口接收和处理消息之后,把控制返回给Windows。Windows系统在同一时间可显示多个窗口。此系统利用消息队列来记录鼠标和键盘输入到相应窗口等消息。如果用户移动鼠标或敲一下键盘,鼠标或键盘的设备驱动器都会把此输入转换成消息,并把它们放到系统消息队列中去。Windows从系统队列中每次读取一条消息, 然后将其从队列中删除,之后处理它们发送到相应的窗口过程。
消息队列是一种先进先出的队列型数据结构,它实际上是系统内核中的一个内部链表。用户可以从消息队列中添加消息和读取消息,而消息则被顺序地插入到队列中。其中发送进程将消息添加到队列末尾,接收进程会从队列头读取消息。
今天我们接之前的《微软MFC技术中的消息队列及消息处理(上)》继续往下讲。
详解具体实例——消息的处理
首先用visual studio 2022新建一个工程名为Win32_message的Win32应用程序。工程建好后,新建一个源文件,命名为 Win32_message.cpp。此程序实现的功能是创建一个窗口,当按键盘上的按键时,会出现一个消息盒子,如“所按键的字符值是98”。如果按下Backspace键时,一旦消息发送成功,会出现有“发送后退消息成功”提示语句的消息盒子,否则,会出现“发送后退消息失败”等类似其他消息响应。我们来看看下面的代码,先看此程序的入口函数WinMain()。
Win32应用程序入口函数示例:Win32_message.cpp
//Win32应用程序入口函数WinMain()
int WINAPI WinMain(
HINSTANCE hInstance, //指向当前实例的句柄
HINSTANCE hPrevInstance, //指向先前实例的句柄
LPSTR lpCmdLine, //命令行
int nCmdShow //显示状态
)
{
WNDCLASS wndclass; //创建一个窗口类
wndclass. cbClsExtra=0; //窗口类无扩展
wndclass. cbWndExtra=0; //窗口实例无扩展
wndclass. hbrBackground=(HBRUSH)GetStockObject (BLACK_BRUSH); //窗口背景为黑
wndclass.hCursor=LoadCursor(NULL,IDC_ARROW); //窗口采用箭头光标
wndclass. hIcon=LoadIcon(NULL, IDI_APPLICATION); //窗口的最小化图标为缺省
wndclass. hInstance=hInstance: //当前实例句柄
wndclass. lpfnWndProc=WinHouProc //定义窗口处理函数
wndclass.lpszClassName="VC++"; //窗口类为“VC++”
wndclass.lpszMenuName=NULL; //窗口无菜单
wndclass.style=CS_HREDRAW|CS_PARENTDC; //设置窗口类型
RegisterClass(&wndclass); //注册窗口类
HWND hwnd;
hwnd=CreateWindow("VC++","消息机制",WS_OVERLAPPEDWINDOW,0,0, 600, 400, NULL, NULL, hInstance, NULL); //创建窗口
ShowWindow(hwnd, SW_SHOWNORMAL); //显示窗口
UpdateWindow(hwnd); //更新窗口
MSG msg;
while(GetMessage 6msg, NULL,0,0)) //获取消息
{
TranslateMessage (&msg);
DispatchMessage (&msg);
}
return 0;
}
从代码可以大致看出此程序的运行机制。要想显示一个窗口并执行各种命令,应先创建一个窗口。第9行对应创建窗口类这一步,而第16行表明将函数 WinHouProc()赋给此窗口的消息响应函数。第20行对应注册窗口类,这样一个窗口类已形成。接下来第23行就要定义一个窗口的大小、位置、窗口显示名字等信息。窗口也创建好之后,第26~27行就要显示窗口并实时更新此窗口的状态。最后第30行就是要处理关联此窗口的各种消息,判断当前是否得到消息,并形成循环。
讨论完入口函数,就要介绍此窗口的消息响应函数,代码为下面所示的消息响应函数WinHouProc()。
Win32应用程序消息响应函数示例:Win32_message.cpp
LRESULT CALLBACK WinHouProc(
HWND hwnd, //窗口句柄
UINT uMsg, //消息标识符
WPARAM WParam, //第一个消息参数
LPARAM lParam //第二个消息参数
)
{
switch(uMsg)
{
case WM_CHAR:
switch(wParam) //显示所按键值
{
case 0x08: //后退键
if(PostMessage (hwnd, WM_KEYDOWN, VK_DELETE, 1L))
MessageBox(hwnd,"发送后退消息成功","VC++",0);
else
MessageBox(hwnd,"发送后退消息失败","VC++",0);
break;
default:
break;
}
char szChar[20];
sprintf(szChar,"所按字符值是%d",wParam); //转换按键值类型
MessageBox(hwnd, szChar, "VC++", 0); //显示按键的值
break;
case WM_CLOSE: //有提示消息的关闭窗口
if(IDYES=MessageBox(hwnd,"是否结束?","VC++",MB_YESNO) //判断
DestroyWindow(hwnd); //銷毀窗口
break;
case WM_DESTROY: //强制关闭
PostQuitMessage(0); //发送关闭消息
break;
default:
return DefWindowProc(hwnd,uMsg,wParam,lParam); //返回窗口过程
}
return 0;
}
代码说明了3种功能:显示按键值、有提示消息的关闭、强制关闭。第14行是PostMessage()函数的应用,如果后退消息发送成功,就提示“发送后退消息成功”。第31行 PostQuitMessage()是 PostMessage()传递关闭消息的一种简单形式。
但是要注意的是,如果是在比VC++6.0和VS2003高级的版本上运行以上所编的程序时,就是立刻报一大堆错,应修改一下工程的编码选项:项目属性——常规——字符集——使用多字节字符集。否认显示文字部分会出现乱码。因为VC++6.0与VS2003默认使用ANSI编码,而VS2022及以上版本默认采用 Unicode。
作者简介:荔园微风,1981年生,高级工程师,浙大工学硕士,软件工程项目主管,做过程序员、软件设计师、系统架构师,早期的Windows程序员,Visual Studio忠实用户,C/C++使用者,是一位在计算机界学习、拼搏、奋斗了25年的老将,经历了UNIX时代、桌面WIN32时代、Web应用时代、云计算时代、手机安卓时代、大数据时代、ICT时代、AI深度学习时代、智能机器时代,我不知道未来还会有什么时代,只记得这一路走来,充满着艰辛与收获,愿同大家一起走下去,充满希望的走下去。