1.窗口
在MiniGUI中,窗口组织为层次体系结构的形式。根窗口是所有窗口的祖先。除根窗口以外的所有窗口都有父窗口,每一个窗口都可能有子窗口、兄弟窗口、祖先窗口和子孙窗口等。在同一级的窗口可以重叠,但是某个时刻只能有一个窗口输出到重叠区域。
MiniGUI中有3种窗口类型:主窗口、对话框和控件窗口(子窗口)。主窗口通常包括一些子窗口,这些子窗口通常是控件窗口,也可以是自定义窗口类。应用程序还会创建其他类型的窗口,如对话框和消息框等。对话框本质上就是主窗口,应用程序一般通过对话框提示用户进行输入操作。
在MiniGUI程序中,调用CreateMainWindow()函数可建立主窗口。建立主窗口之后,程序将进入消息循环。在退出消息循环之后,还要调用MainWindowThreadCleanup()函数来销毁主窗口的消息队列,该函数一般在线程或者进程的最后调用。下面是一个建立主窗口的例子。
【例6-1】窗口的建立。
这是一个在MiniGUI中建立窗口的例子。
设计步骤
1)在Vim中创建一个新工程文件,命名为“example6_1.c”。
2)在“example6_1.c”中创建如下代码。
图6-3 建立的窗口
程序的运行结果如图6-3所示。
利用DestroyMainWindow()函数可以销毁主窗口,但是不会销毁主窗口所使用的消息队列。应用程序要在线程或进程的最后使用MainWindowCleanup()函数,最终清除主窗口所使用的消息队列以及窗口对象本身。一般地,当主窗口接收到MSG_CLOSE消息后会销毁主窗口,并调用PostQuitMessage消息终止消息循环。具体处理方式如下:
2.消息
在MiniGUI中,消息被定义为如下形式:
一个消息由该消息所属的窗口(hwnd)、消息编号(message)、消息的WPARAM型参数(wParam)以及消息的LPARAM型参数(lParam)组成。消息的两个参数中包含了重要的内容。例如,对鼠标消息而言,lParam参数中一般包含鼠标的位置信息,而wParam参数中则包含发生该消息时对应的<Shift>键的状态信息等。对其他不同的消息类型来说,wParam和lParam参数也具有明确的定义。当然,用户也可以自定义消息,并定义消息的wParam和lParam意义。为了使用户能够自定义消息,MiniGUI定义了MSG_USER宏,可如下定义自己的消息:
用户可以在自己的程序中使用自定义消息,并利用自定义消息传递数据。
在MiniGUI中,消息主要分为以下几类:(www.xing528.com)
●系统消息:包括MSG_IDLE、MSG_TIMER和MSG_FDEVENT等。
●对话框消息:包括MSG_COMMAND、MSG_INITDIALOG、MSG_ISDIALOG、MSG_SETTEXT、MSG_GETTEXT和MSG_FONTCHANGED等。
●窗口绘制消息:包括MSG_PAINT和MSG_ERASEBKGND等。
●键盘和鼠标消息:包括MSG_KEYDOWN、MSG_CHAR、MSG_LBUTTONDOWN和MSG_MOUSEMOVE等。
●键盘和鼠标后处理消息:包括MSG_SETCURSOR、MSG_SETFOCUS、MSG_KILLFOCUS和MSG_MOUSEMOVEIN等由键盘和鼠标消息产生的窗口事件消息。
●投递消息:MiniGUI事件驱动机制首先使用PostMessage()函数把消息投递到消息队列中。PostMessage()函数投递完成后就立即返回,准备下一条消息的投递。如果消息队列缓冲区已满,则PostMessage()函数会返回一个错误值。为了防止缓冲区已满造成的消息丢失,可以使用SendNotifyMessage()函数进行消息投递。投递消息常用于处理键盘和鼠标消息。
●发生消息:MiniGUI事件驱动机制使用SendMessage()函数把消息直接发送给窗口过程函数,该函数等待窗口过程函数处理完消息后再返回。在MiniGUI中,事件驱动机制把消息发送到窗口过程函数时有两种方式:一种是把消息放入消息队列,这种方式称为投递消息;另一种是把消息直接发送给窗口过程函数,这种方式称为发送消息。
3.消息循环
简言之,消息循环就是一个循环体,在这个循环体中,程序利用GetMessage()函数不停地从消息队列中获得消息,然后利用DispatchMessage()函数将消息发送到指定的窗口,也就是调用指定窗口的消息处理函数,并传递消息及其参数。典型的消息循环如下所示:
可见,GetMessage()函数从hMainWnd窗口所属的消息队列中获得消息,然后调用TranslateMessage()函数将MSG_KEYDOWN和MSG_KEYUP消息翻译成MSG_CHAR消息,最后调用DispatchMessage()函数将消息发送到指定的窗口。
在MiniGUI-Threads版本中,每个建立有窗口的GUI线程都有自己的消息队列,而且所有属于同一线程的窗口共享同一个消息队列。因此,GetMessage()函数将获得所有与hMainWnd窗口在同一线程中的窗口的消息。
而在MiniGUI-Lite版本中只有一个消息队列,GetMessage()函数将从该消息队列当中获得所有的消息,而忽略hMainWnd参数。
除了上面提到的GetMessage()、TranslateMessage()和DispatchMessage()函数以外,MiniGUI还支持如下几个消息处理函数。
(1)PostMessage()函数
该函数将消息放到指定窗口的消息队列后立即返回。这种发送方式称为“邮寄”消息。如果消息队列中的邮寄消息缓冲区已满,则该函数返回错误值。在下一个消息循环中,由GetMessage()函数获得这个消息之后,窗口才会处理该消息。PostMessage()一般用于发送一些非关键性的消息。例如,在MiniGUI中,鼠标和键盘消息就是通过PostMessage()函数发送的。
(2)SendMessage()函数
该函数和PostMessage()函数不同,它在发送一条消息给指定窗口时,将等待该消息被处理后才会返回。当需要知道某个消息的处理结果时,使用该函数发送消息,然后根据其返回值进行处理。在MiniGUI-Threads中,如果发送消息的线程和接收消息的线程不是同一个线程,发送消息的线程将阻塞并等待另一个线程的处理结果,然后继续运行;否则,SendMessage()函数将直接调用接收消息窗口的窗口过程函数。MiniGUI-Lite则和上面的第2种情况一样,直接调用接收消息窗口的窗口过程函数。
(3)SendNotifyMessage()函数
该函数和PostMessage()函数类似,也是不等待消息被处理即返回。但和PostMessage()函数不同的是,通过该函数发送的消息不会因为缓冲区满而丢失,因为系统采用链表的形式处理这种消息。通过该函数发送的消息称为“通知消息”,一般用来从控件向其父窗口发送通知消息。
(4)PostQuitMessage()函数
该消息在消息队列中设置一个QS_QUIT标志。GetMessage在从指定消息队列中获取消息时会检查该标志。如果有QS_QUIT标志,GetMessage消息将返回FALSE,从而可以利用该返回值终止消息循环。
在MiniGUI中通常使用消息驱动作为应用程序的创建架构。应用程序一般需要提供一个处理消息的标准函数。在消息循环中,系统可以调用此函数,应用程序在此函数中处理相应的消息。图6-4所示是一个消息驱动的应用程序架构示意图。
图6-4 消息驱动的应用程序架构示意图
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。