miniGUI源码分析:消息机制
miniGUI通过接收消息来和外界交互。消息由系统或应用程序产生,系统对输入事件产生消息,系统对应用程序的响应也会产生消息,应用程序可以通过产生消息来完成某个任务,或者与其它应用程序的窗口进行通讯。总而言之,miniGUI 是消息驱动的系统,一切运作都围绕着消息进行。
MSG 消息结构
MSG 消息结构的成员包括该消息所属的窗口(hwnd)、消息标识(message)、消息的WPARAM 型参数(wParam)、消息的 LPARAM 型参数(lParam)以及消息发生的时间。
typedef struct _MSG{HWND hwnd;int message;WPARAM wParam;LPARAM lParam;unsigned int time;}MSG;typedef MSG* PMSG;
窗口句柄决定消息所发送的目标窗口。消息标识是一个整数常量,由它来标明消息的类型,每一个消息均有一个对应的预定义标识符。消息的参数对消息的内容作进一步的说明,它的意义通常取决于消息本身,可以是一个整数、位标志或数据结构指针等。
MSGQUEUE消息队列
miniGUI 是消息驱动的系统,消息驱动的含义就是,程序的流程不再是只有一个入口和若干个出口的串行执行线路;相反,程序会一直处于一个循环状态,在这个循环当中,程序不断从外部或内部获取某些事件,比如用户的按键或者鼠标的移动,然后根据这些事件作出某种响应,并完成一定的功能,这个循环直到程序接收到某个消息为止。它的底层实现就是靠消息队列。
typedef struct _QMSG
{MSG Msg;struct _QMSG* next;
}QMSG;
typedef QMSG* PQMSGstruct _MSGQUEUE
{LWORD dwState;PQMSG pFirstNotifyMsg;PQMSG pLastNotifyMsg;MSG* msg;int len;int readpos, writepos;WORD TimerMask;int loop_depth;
}
消息队列结构_MSGQUEUE的成员包含消息队列类型(dwState)、NOTIFY消息链表表头(pFirstNotifyMsg)、NOTIFY消息链表表尾(pLastNotifyMsg)、POST消息缓冲区(msg)、POST消息缓冲区大小(len)、消息队列读写标志位(readpos, writepos)。
初始化消息队列
BOOL InitMsgQueue (PMSGQUEUE pMsgQueue, int iBufferLen)
{memset (pMsgQueue, 0, sizeof(MSGQUEUE));pMsgQueue->dwState = QS_EMPTY;if (iBufferLen <= 0)iBufferLen = DEF_MSGQUEUE_LEN;pMsgQueue->msg = malloc (sizeof (MSG) * iBufferLen);if (pMsgQueue->msg){memset(pMsgQueue->msg, 0, sizeof (MSG) * iBufferLen);}pMsgQueue->len = iBufferLen;pMsgQueue->TimerMask = 0;return TRUE;
}
消息队列的初始化主要将dwState赋值为QS_EMPTY类型,并开辟iBufferLen大小的缓冲区,DEF_MSGQUEUE_LEN默认值为16。
发送消息
miniGUI 有两种向窗口过程发送消息的办法:
- 把消息投递到一个先进先出的消息队列中,它是系统中用于存储消息的一块内存区域,每个消息存储在一个消息结构中。
- 或是把消息直接发送给窗口过程,也就是通过消息发送函数直接调用窗口过程函数。
miniGUI有三个主要的发送消息函数
- PostMessage
该函数将消息放到指定窗口的消息队列后立即返回。这种发送方式称为“邮寄”消息。如果消息队列中的邮寄消息缓冲区已满,则该函数返回错误值。PostMessage 一般用于发送一些非关键性的消息。比如在 MiniGUI 中,鼠标和键盘消息就是通过PostMessage 函数发送的。
PostMessage通过调用GetMsgQueue获取消息队列,并调用QueueMessage将消息写入消息队列。消息队列的读取会记录readpos和writepos标志位,当写入一个消息writepos就加1,读了一个消息readpos就加1,如果readpos和writepos相等就表示没有新消息,默认消息队列一次最多存msg_que->len的消息,通常为默认值DEF_MSGQUEUE_LEN,消息满了的话就会丢弃。QueueMessage函数把消息写入队列中,writepos加1,超过最大数之后就归零。
int GUIAPI PostMessage (HWND hWnd, int iMsg, WPARAM wParam, LPARAM lParam)
{PMSGQUEUE pMsgQueue = NULL;MSG msg;if (!(pMsgQueue = GetMsgQueue(hWnd))){return ERR_INV_HWND;}memset(&msg, 0, sizeof(msg));msg.hwnd = hWnd;msg.message = iMsg;msg.wParam = wParam;msg.lParam = lParam;msg.time = 0;if (!QueueMessage(pMsgQueue, &msg)){return ERR_QUEUE_FULL;}return ERR_OK;
}BOOL QueueMessage (PMSGQUEUE msg_que, PMSG msg)
{LOCK_MSGQ(msg_que);if ((msg_que->writepos + 1) % msg_que->len == msg_que->readpos) {UNLOCK_MSGQ(msg_que);return FALSE;}msg_que->msg [msg_que->writepos] = *msg;msg_que->writepos++;if (msg_que->writepos >= msg_que->len){msg_que->writepos = 0;}msg_que->dwState |= QS_POSTMSG;UNLOCK_MSGQ (msg_que);return TRUE;
}
- SendNotifyMessage
该函数和 PostMessage 消息类似,也是不等待消息被处理即返回。但和 PostMessage 消息不同,通过该函数发送的消息不会因为缓冲区满而丢失,因为系统采用链表的形式处理这种消息。通过该函数发送的消息称为“通知消息”,一般用来从控件向其父窗口发送通知消息。
SendNotifyMessage 函数将消息写入消息队列中的链表QMSG中,发送Notify消息时会创建链表节点,并接入链表,通过维护链表表头pFirstNotifyMsg和表尾pLastNotifyMsg指针记录消息。
int GUIAPI SendNotifyMessage (HWND hWnd, int iMsg, WPARAM wParam, LPARAM lParam)
{PMSGQUEUE pMsgQueue;PQMSG pqmsg;if (!(pMsgQueue = GetMsgQueue(hWnd)))return ERR_INV_HWND;pqmsg = QMSGAlloc();if (pqmsg == NULL) {return ERR_RES_ALLOCATION;}LOCK_MSGQ (pMsgQueue);pqmsg->Msg.hwnd = hWnd;pqmsg->Msg.message = iMsg;pqmsg->Msg.wParam = wParam;pqmsg->Msg.lParam = lParam;pqmsg->next = NULL;if (pMsgQueue->pFirstNotifyMsg == NULL) {pMsgQueue->pFirstNotifyMsg = pMsgQueue->pLastNotifyMsg = pqmsg;}else {if (pMsgQueue->pLastNotifyMsg == NULL) {pMsgQueue->pLastNotifyMsg = pqmsg;}else {pMsgQueue->pLastNotifyMsg->next = pqmsg;pMsgQueue->pLastNotifyMsg = pqmsg;}}pMsgQueue->dwState |= QS_NOTIFYMSG;UNLOCK_MSGQ (pMsgQueue);return ERR_OK;
}
- SendMessage
该函数和 PostMessage 函数不同,它把一条消息发送给指定窗口的窗口过程,而且等待该窗口过程完成消息的处理之后才会返回。当需要知道某个消息的处理结果时,使用该函数发送消息,然后根据其返回值进行处理。
SendMessage函数通过调用GetWndProc获取窗口过程函数,并直接调用窗口过程函数处理消息,处理结束后返回。
int GUIAPI SendMessage(HWND hWnd, int iMsg, WPARAM wParam, LPARAM lParam)
{...//省略WndProc = GetWndProc(hWnd) //获取消息处理函数(*WndProc)(hWnd, iMsg, wParam, lParam) //直接处理消息并返回...
}
处理消息
程序会一直处于一个循环状态,在这个循环当中,不断从消息队列中获取消息并处理,直到程序接收到退出消息QS_QUIT为止。GetMessage从消息队列中取出消息,主要调用PeekMessageEx函数处理,PeekMessageEx处理消息时会按以下优先级处理:QS_QUIT、QS_NOTIFYMSG、QS_POSTMSG、QS_PAINT、QS_DESKTIMER。TranslateMessage把击键消息转换为 MSG_CHAR 消息,然后直接发送到窗口过程函数。DispatchMessage获取窗口过程函数并直接调用处理消息。下面主要介绍PeekMessageEx函数如何处理消息队列中的NOTIFYMSG消息和POSTMSG消息。
while (GetMessage (&Msg, hWnd))
{if( !TranslateMessage (&Msg) ){}if( !DispatchMessage (&Msg) ){}
}BOOL PeekMessageEx (PMSG pMsg, HWND hWnd, int iMsgFilterMin, int iMsgFilterMax, BOOL bWait, UINT uRemoveMsg)
{PMSGQUEUE pMsgQueue;PQMSG phead;int slot;PMG_TDEINFO pstruTdeInfo = NULL;#ifndef _LITE_VERSIONif (!(thi = GetThreadInfo ((pthread_t)HPR_Thread_GetSelfId()))){return FALSE;}pMsgQueue = thi->pMsgQueue;
#elsepMsgQueue = __mg_dsk_msg_queue;
#endifmemset (pMsg, 0, sizeof(MSG));LOCK_MSGQ (pMsgQueue);//QS_QUITif (pMsgQueue->dwState & QS_QUIT) {pMsg->hwnd = hWnd;pMsg->message = MSG_QUIT;pMsg->wParam = 0;pMsg->lParam = 0;SET_PADD (NULL);if (uRemoveMsg == PM_REMOVE) {pMsgQueue->loop_depth --;if (pMsgQueue->loop_depth == 0)pMsgQueue->dwState &= ~QS_QUIT;}UNLOCK_MSGQ (pMsgQueue);return FALSE;}//QS_NOTIFYMSGif (pMsgQueue->dwState & QS_NOTIFYMSG) {if (pMsgQueue->pFirstNotifyMsg) {phead = pMsgQueue->pFirstNotifyMsg;*pMsg = phead->Msg;SET_PADD (NULL);if (IS_MSG_WANTED(pMsg->message)) {if (uRemoveMsg == PM_REMOVE) {pMsgQueue->pFirstNotifyMsg = phead->next;FreeQMSG (phead);}UNLOCK_MSGQ (pMsgQueue);return TRUE;}}elsepMsgQueue->dwState &= ~QS_NOTIFYMSG;}//QS_POSTMSGif (pMsgQueue->dwState & QS_POSTMSG) {if (pMsgQueue->readpos != pMsgQueue->writepos) { *pMsg = pMsgQueue->msg[pMsgQueue->readpos];SET_PADD (NULL);if (IS_MSG_WANTED(pMsg->message)) {if (uRemoveMsg == PM_REMOVE) {pMsgQueue->readpos++;if (pMsgQueue->readpos >= pMsgQueue->len)pMsgQueue->readpos = 0;}UNLOCK_MSGQ (pMsgQueue);return TRUE;}}elsepMsgQueue->dwState &= ~QS_POSTMSG;}...
}
- 当获取的消息类型为QS_QUIT时,返回FALSE结束循环。
- 当获取的消息类型为QS_NOTIFYMSG时,则从NOTIFY消息链表表头取出消息,并将表头指针指向链表下一节点,释放表头节点空间。
- 当获取的消息类型为QS_POSTMSG时,首先通过判断readpos和writepos是否相等确认POST消息缓冲区中是否有新消息,从readpos位置读取消息后将readpos加1,若readpos超过默认大小则置为0,下次读取时则从缓冲区开始位置。
miniGUI源码分析:消息机制相关推荐
- MyBatis 源码分析 - 插件机制
1.简介 一般情况下,开源框架都会提供插件或其他形式的拓展点,供开发者自行拓展.这样的好处是显而易见的,一是增加了框架的灵活性.二是开发者可以结合实际需求,对框架进行拓展,使其能够更好的工作.以 My ...
- 源码解析——消息机制
映象笔记的链接:源码解析--消息机制 本文转自wauoen51CTO博客,原文链接: http://blog.51cto.com/7183397/1968269,如需转载请自行联系原作者
- 源码分析Handler机制
看下面例子: 在子线程发送一个消息,然后在主线程街道这个消息处理,这个消息是如何从子线程切换到主线程的呢?首先跟踪一下handler.sendMessage(new Message())如下: 从上面 ...
- Dubbo 源码分析 - SPI 机制
1.简介 SPI 全称为 Service Provider Interface,是一种服务发现机制.SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类.这样可以 ...
- JQuery源码分析 - 闭包机制在jQuery中的使用及冲突解决
jQuery中的闭包机制 本系列中我们将基于jquery3.5.1版本对jQuery源码进行分析,分析以源码加注释的方式展示. 本节中将分析jQuery源码中的 14 ~ 40行:自执行函数定义.环境 ...
- SOFA 源码分析 — 扩展机制
前言 我们在之前的文章中已经稍微了解过 SOFA 的扩展机制,我们也说过,一个好的框架,必然是易于扩展的.那么 SOFA 具体是怎么实现的呢? 一起来看看. 如何使用? 看官方的 demo: 1.定义 ...
- miniGUI源码分析:初始化
前言 miniGUI是一个面向嵌入式系统的轻量级图形用户界面支持系统,其轻量小巧,占用资源少是主要优势.但由于没有更新,目前使用的仍是08年发布的版本,随着用户对操作界面的美观度及视觉效果的要求越来越 ...
- Android 系统(177)---Android消息机制分析:Handler、Looper、MessageQueue源码分析
Android消息机制分析:Handler.Looper.MessageQueue源码分析 1.前言 关于Handler消息机制的博客实际上是非常多的了. 之前也是看别人的博客过来的,但是过了一段时间 ...
- Memcached源码分析 - 内存存储机制Slabs(5)
Memcached源码分析 - 网络模型(1) Memcached源码分析 - 命令解析(2) Memcached源码分析 - 数据存储(3) Memcached源码分析 - 增删改查操作(4) Me ...
最新文章
- WindowsServer2012史记3-SMB管理
- linux c ip数据包,如何在Linux上的C / C ++中使用ipv6 udp套接字进行多播?
- Android动画模式
- java控制cmd导出dmp文件_cmd的操作命令导出导入.dmp文件
- 图形桌面linux触摸,新手看招:用图形桌面访问Linux操作系统
- qdir安装 多窗口资源管理软件
- label mpchart 饼图_Android MPChart—饼图-Go语言中文社区
- c++ memset 语言_C/C++ 中memset() 函数详解及其作用介绍
- 三相四线怎样查漏电_漏电保护器的选用
- Django项目实践2 - Django模板语言(常用语法规则)
- Windows 10 不同版本WHQL认证驱动数字签名兼容问题
- 5.21 ticker的使用
- php验证码实现的代码怎么写,php验证码实现代码
- python路径、工作路径、文件路径问题、改变当前路径
- 如何设置Raspberry Pi Zero进行旅行
- 计算机设计大赛指导老师的申报书,附件大赛作品申报书.DOC
- 清华大学计算机复试录取率,清华考研非985不要吗 复试淘汰率高不高
- 判定南京配眼镜哪家好的三步骤,轻松解决配镜烦恼
- Android10 FFmpeg开发案例之实现一个简易视频编辑器
- 联想乐檬k3刷安卓5.1教程
热门文章
- 中国20顶级富豪惊人挥霍排行榜 仅一家IT老板
- 用tikz画球坐标系下的体积微元
- 比较线程子进程 占用的内存情况
- MSI(Message Signaled Interrupt)/MSI-X
- 【Java爬虫】爬取南通大学教务处成绩
- SpringBoot的幕后推手,五年Java开发者小米、阿里面经
- 关于简单控件RadioButtonList的使用
- html手机打不开是什么意思,html是什么意思
- mysql1526_MySQL数据库插入数据出现 ERROR 1526 (HY000): Table has no partition for value xxx
- 1526. N叉树的前序遍历