Win32 SDK基础(八)之 关于Windows消息机制的详解(图)

发布时间 - 2017-06-06 00:00:00    点击率:

一、 什么是消息

        在解释什么是消息之前,我们先讨论一下程序的执行机制问题。大体上说,程序按照执行机制可以分为两类:
第一类是过程驱动。比如我们最早接触编程时写的c程序,又或者单片机程序。这类程序往往预先已经设定好了执行流程,我们执行时只是按部就班的执行;
第二类是事件驱动。事件,相信大家都能够理解。每个事件的发生都是随机的,每个事件都会有发生的时刻,类似生活中的事件。程序中的事件也会有自己的触发点,事件驱动程序就是事先编写好了针对每个事件的处理流程。在windows的操作系统中,消息就是windows中的事件。windows中的几乎每个操作都会触发消息,像我们之前讲过的创建窗口会触发wm_create消息,绘制窗口会触发wm_paint消息,我们点击鼠标、键盘、都会触发相应的消息。
        windows的消息被封装成了一个叫做msg的结构体,其原型如下:

typedef struct tagMSG { // msg 
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG;

Hwnd —— 触发消息的窗口的句柄。
Message —— 消息ID。Windows操作系统为每个消息都分配了一个消息ID,这个ID是唯一的。我们在上文中提到过的WM_CREATE本质就是一个整数,就是消息ID。
wParam —— 消息可附带的参数。
lParam —— 消息可附带的参数。
Time —— 发生消息的时刻。
Pt —— 发生消息时鼠标所在的位置。
以上参数对消息来说,缺一不可。
Windows中就是将一个个消息封装成MSG对象,发送消息时,将这些对象放置到消息队列中;获取消息时,也是获取的这些MSG对象。

二、 消息获取

2.1 消息队列

        我们说过,在Windows中几乎每个操作都会触发一条消息,这些消息都被发送到消息队列中。何为消息队列?我们可以将其理解为使用了一个存放Msg对象的先进先出的Deque—— Deque。消息队列分为两种,一种是系统消息队列,另外一种是进程消息队列。我们在触发消息后,消息先进入系统消息队列。操作系统处理后会根据消息的的窗口句柄hwnd值将消息分配到我们程序自己的消息队列,然后在我们程序内部进行消息的处理。

2.2 消息循环

        在前面的文章中,我们曾经写过一个消息循环。所谓的消息循环,就是不断的读取我们的进程中的消息队里中的消息,然后在进行处理。

void Message()  
{  
    MSG nMsg = { 0 };  
    while (GetMessage(&nMsg, NULL, 0, 0))  
    {  
        TranslateMessage(&nMsg);  
        DispatchMessage(&nMsg);  
    }  
}

        这里面,GetMessage()不断的在消息队列中抓取消息,其函数原型如下:
GetMessage(LPMSG lpMsg,HWND hWnd,UINT wMsgFilterMin,UINT wMsgFilterMax)
lpMsg —— 用来存放消息的MSG类型的指针。
hWnd —— 指定取得其消息的窗口的句柄。当其值取NULL时,GetMessage为任何属于调用线程的窗口检索消息。
wMsgFilterMin —— 指定被检索的最小消息值的整数。
wMsgFilterMax —— 指定被检索的最大消息值的整数。

        GetMessage()获取到消息后,TranslateMessage会将消息进行翻译,主要是把虚拟键消息转换为字符消息。字符消息被寄送到调用线程的消息队列里,当下一次线程调用函数GetMessage或PeekMessage时被读出。Windows中每一个键盘按键,都对应了一个宏,这个键盘按键发出的消息就是虚拟键消息。TranslateMessage的作用就是将虚拟键消息转成字符消息WM_CHAR、WM_SYSCHAR等等。

三、消息处理

        DispatchMessage的作用就是将消息,分派到我们实现定义好的窗口处理函数中进行处理,下面是我们在之前的文章中定义的窗口处理函数:

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)  
{  
	switch (uMsg)  
	{  
	case WM_DESTROY:  
		PostQuitMessage(0);//可¨¦以°?使º1GetMessage返¤¦Ì回?0  
		break;  
	default:  
		break;  
	}  
	return DefWindowProc(hWnd, uMsg, wParam, lParam);  
}


        hWnd就是产生消息的窗口句柄,uMsg是传递的消息,wParam和lParam分别是消息携带的两个参数。在上面的窗口处理函数中,我们定只处理了一个消息WM_DESTROY,这是我们在点击窗口的关闭按钮后产生的一个消息。我们说过,我们在创建窗口是,也会产生一个WM_CREATE消息。下面我们在窗口处理函数中处理这个消息:

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)  
{  
	switch (uMsg)  
	{  
	case WM_DESTROY:  
		PostQuitMessage(0);//可¨¦以°?使º1GetMessage返¤¦Ì回?0  
		break; 
	case  WM_CREATE:
		MessageBox(NULL,"WM_CREATE消息被处理了","消息处理",MB_OK);
	default:  
		break;  
	}  
	return DefWindowProc(hWnd, uMsg, wParam, lParam);  
}


        我们在接受到WM_CREATE后,会弹出一个对话框。预期的效果是点击这个对话框的确定按钮后才会显示窗口。如下面所示:

        运行程序,先弹出对话框:


        点击确定按钮后,弹出窗口:



# 句柄  # 自己的  # 对话框  # 会有  # 好了  # 说过  # 弹出  # 操作系统  # 都是  # 这是 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 如何彻底卸载建站之星软件?  Laravel怎么进行数据库回滚_Laravel Migration数据库版本控制与回滚操作  详解Android——蓝牙技术 带你实现终端间数据传输  个人网站制作流程图片大全,个人网站如何注销?  Laravel如何实现数据导出到PDF_Laravel使用snappy生成网页快照PDF【方案】  laravel怎么在请求结束后执行任务(Terminable Middleware)_laravel Terminable Middleware请求结束任务执行方法  如何在宝塔面板中修改默认建站目录?  Laravel如何使用Seeder填充数据_Laravel模型工厂Factory批量生成测试数据【方法】  JS实现鼠标移上去显示图片或微信二维码  怎样使用JSON进行数据交换_它有什么限制  Laravel队列任务超时怎么办_Laravel Queue Timeout设置详解  Laravel怎么实现搜索功能_Laravel使用Eloquent实现模糊查询与多条件搜索【实例】  Laravel怎么判断请求类型_Laravel Request isMethod用法  企业在线网站设计制作流程,想建设一个属于自己的企业网站,该如何去做?  Java类加载基本过程详细介绍  如何用西部建站助手快速创建专业网站?  Laravel Seeder怎么填充数据_Laravel数据库填充器的使用方法与技巧  Laravel中的withCount方法怎么高效统计关联模型数量  Mybatis 中的insertOrUpdate操作  Laravel Blade模板引擎语法_Laravel Blade布局继承用法  如何挑选高效建站主机与优质域名?  Win11怎么开启自动HDR画质_Windows11显示设置HDR选项  Laravel如何与Pusher实现实时通信?(WebSocket示例)  Laravel如何设置自定义的日志文件名_Laravel根据日期或用户ID生成动态日志【技巧】  Laravel storage目录权限问题_Laravel文件写入权限设置  小米17系列还有一款新机?主打6.9英寸大直屏和旗舰级影像  详解一款开源免费的.NET文档操作组件DocX(.NET组件介绍之一)  Laravel怎么多语言本地化设置_Laravel语言包翻译与Locale动态切换【手册】  Laravel如何集成微信支付SDK_Laravel使用yansongda-pay实现扫码支付【实战】  Laravel如何实现全文搜索_Laravel Scout集成Algolia或Meilisearch教程  网站页面设计需要考虑到这些问题  CSS3怎么给轮播图加过渡动画_transition加transform实现【技巧】  音响网站制作视频教程,隆霸音响官方网站?  Android实现代码画虚线边框背景效果  Laravel Sail是什么_基于Docker的Laravel本地开发环境Sail入门  Swift中switch语句区间和元组模式匹配  如何快速搭建高效WAP手机网站?  JavaScript如何实现类型判断_typeof和instanceof有什么区别  如何用低价快速搭建高质量网站?  b2c电商网站制作流程,b2c水平综合的电商平台?  Laravel怎么集成Log日志记录_Laravel单文件与每日日志配置及自定义通道【详解】  Laravel项目结构怎么组织_大型Laravel应用的最佳目录结构实践  Edge浏览器如何截图和滚动截图_微软Edge网页捕获功能使用教程【技巧】  如何用ChatGPT准备面试 模拟面试问答与职场话术练习教程  Laravel怎么实现支付功能_Laravel集成支付宝微信支付  如何快速生成凡客建站的专业级图册?  如何在万网利用已有域名快速建站?  WordPress 子目录安装中正确处理脚本路径的完整指南  如何在 React 中条件性地遍历数组并渲染元素  国美网站制作流程,国美电器蒸汽鍋怎么用官方网站?