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有三个主要的发送消息函数

  1. 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;
}
  1. 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;
}
  1. 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源码分析:消息机制相关推荐

  1. MyBatis 源码分析 - 插件机制

    1.简介 一般情况下,开源框架都会提供插件或其他形式的拓展点,供开发者自行拓展.这样的好处是显而易见的,一是增加了框架的灵活性.二是开发者可以结合实际需求,对框架进行拓展,使其能够更好的工作.以 My ...

  2. 源码解析——消息机制

    映象笔记的链接:源码解析--消息机制 本文转自wauoen51CTO博客,原文链接: http://blog.51cto.com/7183397/1968269,如需转载请自行联系原作者

  3. 源码分析Handler机制

    看下面例子: 在子线程发送一个消息,然后在主线程街道这个消息处理,这个消息是如何从子线程切换到主线程的呢?首先跟踪一下handler.sendMessage(new Message())如下: 从上面 ...

  4. Dubbo 源码分析 - SPI 机制

    1.简介 SPI 全称为 Service Provider Interface,是一种服务发现机制.SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类.这样可以 ...

  5. JQuery源码分析 - 闭包机制在jQuery中的使用及冲突解决

    jQuery中的闭包机制 本系列中我们将基于jquery3.5.1版本对jQuery源码进行分析,分析以源码加注释的方式展示. 本节中将分析jQuery源码中的 14 ~ 40行:自执行函数定义.环境 ...

  6. SOFA 源码分析 — 扩展机制

    前言 我们在之前的文章中已经稍微了解过 SOFA 的扩展机制,我们也说过,一个好的框架,必然是易于扩展的.那么 SOFA 具体是怎么实现的呢? 一起来看看. 如何使用? 看官方的 demo: 1.定义 ...

  7. miniGUI源码分析:初始化

    前言 miniGUI是一个面向嵌入式系统的轻量级图形用户界面支持系统,其轻量小巧,占用资源少是主要优势.但由于没有更新,目前使用的仍是08年发布的版本,随着用户对操作界面的美观度及视觉效果的要求越来越 ...

  8. Android 系统(177)---Android消息机制分析:Handler、Looper、MessageQueue源码分析

    Android消息机制分析:Handler.Looper.MessageQueue源码分析 1.前言 关于Handler消息机制的博客实际上是非常多的了. 之前也是看别人的博客过来的,但是过了一段时间 ...

  9. Memcached源码分析 - 内存存储机制Slabs(5)

    Memcached源码分析 - 网络模型(1) Memcached源码分析 - 命令解析(2) Memcached源码分析 - 数据存储(3) Memcached源码分析 - 增删改查操作(4) Me ...

最新文章

  1. WindowsServer2012史记3-SMB管理
  2. linux c ip数据包,如何在Linux上的C / C ++中使用ipv6 udp套接字进行多播?
  3. Android动画模式
  4. java控制cmd导出dmp文件_cmd的操作命令导出导入.dmp文件
  5. 图形桌面linux触摸,新手看招:用图形桌面访问Linux操作系统
  6. qdir安装 多窗口资源管理软件
  7. label mpchart 饼图_Android MPChart—饼图-Go语言中文社区
  8. c++ memset 语言_C/C++ 中memset() 函数详解及其作用介绍
  9. 三相四线怎样查漏电_漏电保护器的选用
  10. Django项目实践2 - Django模板语言(常用语法规则)
  11. Windows 10 不同版本WHQL认证驱动数字签名兼容问题
  12. 5.21 ticker的使用
  13. php验证码实现的代码怎么写,php验证码实现代码
  14. python路径、工作路径、文件路径问题、改变当前路径
  15. 如何设置Raspberry Pi Zero进行旅行
  16. 计算机设计大赛指导老师的申报书,附件大赛作品申报书.DOC
  17. 清华大学计算机复试录取率,清华考研非985不要吗 复试淘汰率高不高
  18. 判定南京配眼镜哪家好的三步骤,轻松解决配镜烦恼
  19. Android10 FFmpeg开发案例之实现一个简易视频编辑器
  20. 联想乐檬k3刷安卓5.1教程

热门文章

  1. 中国20顶级富豪惊人挥霍排行榜 仅一家IT老板
  2. 用tikz画球坐标系下的体积微元
  3. 比较线程子进程 占用的内存情况
  4. MSI(Message Signaled Interrupt)/MSI-X
  5. 【Java爬虫】爬取南通大学教务处成绩
  6. SpringBoot的幕后推手,五年Java开发者小米、阿里面经
  7. 关于简单控件RadioButtonList的使用
  8. html手机打不开是什么意思,html是什么意思
  9. mysql1526_MySQL数据库插入数据出现 ERROR 1526 (HY000): Table has no partition for value xxx
  10. 1526. N叉树的前序遍历