分类:
2006-06-08 11:09:19
在Windows系列操作系统中,广泛使用了消息驱动的概念。在MiniGUI中,我们也使用了消息驱动作为应用程序的创建构架。
在消息驱动的应用程序中,计算机外设发生的事件,例如键盘键的敲击、鼠标键的按击等,都由支持系统收集,将其以事先的约定格式翻译为特定的消息。应用程序一般包含有自己的消息队列,系统将消息发送到应用程序的消息队列中。应用程序可以建立一个循环,在这个循环中读取消息并处理消息,一直处理到特定的消息传来为止。这样的循环称为消息循环。一般地,消息由代表消息的一个整型数和消息的附加参数组成。例如,鼠标左键的按下消息,可能由133这个数来表示,其附加参数可能包含按下时的鼠标所在位置信息。例如,MiniGUI中如下定义消息:
typedef struct
{
HWND hwnd;
int message;
WPARAM wParam;
LPARAM lParam;
...
}MSG;
message 指定了特定的消息类型,wParam 是以unsigned int类型定义的消息的短参数,lParam 是以 long 类型定义的消息长参数。
应用程序一般要提供一个处理消息的标准函数。在消息循环中,系统可以调用此函数,应用程序在此函数中处理相应消息。
图 1.2是一个消息驱动的应用程序的简单构架示意。
图 1.2 消息驱动的应用程序的简单构架
在 MiniGUI 中,消息分为如下几种类型:
ü 系统消息,为系统内部管理使用。
ü 鼠标消息,鼠标的点击、移动等产生的消息。
ü 键盘消息,键盘的按键消息。
ü 窗口消息,窗口管理消息。
ü 菜单消息,菜单管理消息。
ü 命令消息等。
窗口过程是用来处理窗口消息的函数过程。对于同一类型的控件,其窗口过程一般是一样的。因此,系统一般利用窗口的窗口类名来区分不同的窗口类并调用不同的窗口过程。由于几乎每一个主窗口均和其他窗口有着不同的窗口过程,因此,在 MiniGUI 中,窗口类的概念只存在于控件和窗片中。对于主窗口来说,其窗口过程在建立主窗口时指定,而对控件和窗片来说,则在注册窗口类时指定,而在建立窗片或控件时指定所属窗口类。
句柄是 MiniGUI 用来标识对象的标识符。句柄和指针概念类似,但它不一定是指针值。利用句柄,MiniGUI 将系统变量从应用项目中分离了出来,因为程序员只能通过句柄访问对象,因而就没有利用指针是可能发生的因非法访问而导致的数据不一致问题。
在 MiniGUI 中,窗口、控件、设备环境、菜单、图标等均使用句柄访问。
我们将基于 MiniGUI 的一个会话(session)称为一个应用项目,而其中每个单独的线程或线程组称为应用。每个应用项目可建立多个应用。主窗口是建立在 MiniGUI 基础上的应用的主界面。MiniGUI 为每个主窗口建立单独的消息队列,在该主窗口基础上派生出的窗片、对话框及其控件均使用同一消息队列。在 MiniGUI 中,每个应用对应于一个线程。理论上讲,每个应用可以具备多个主窗口,但在 MiniGUI 中,主窗口均以单独的线程实现。但多个主窗口对应单一线程的情况也是可以在 MiniGUI 中实现的。
每个应用项目有一个 MiniGUIMain 函数,在这个函数中,可建立初始的应用线程。在调用 MiniGUIMain 之前,MiniGUI 启动自己的桌面窗口(Desktop)。桌面窗口作为 MiniGUI 的窗口管理器而存在。下面的代码段在 MiniGUIMain 中启动了三个主窗口线程:
int MiniGUIMain(int args, char* arg[])
{
pthread_t thread, thread2, thread3;
CreateThreadForMainWindow(&thread, NULL, TestWindowMain, 0);
CreateThreadForMainWindow(&thread2, NULL, TestWindowMain2, 0);
CreateThreadForMainWindow(&thread3, NULL, TestWindowMain3, 0);
return 0;
}
CreateThreadForMainWindow 函数为主窗口建立线程,并返回线程标识符。
其中的第三个参数是线程的入口函数地址。如下的代码段定义了上述代码中第一个主窗口线程的入口函数:
void InitCreateInfo(PMAINWINCREATE pCreateInfo)
{
pCreateInfo->dwStyle = WS_THICKFRAME;
pCreateInfo->spCaption = "The first main window" ;
pCreateInfo->hMenu = 0;
pCreateInfo->hCursor = GetSystemCursor(2);
pCreateInfo->hIcon = LoadIconFromFile("res/table.ico");
pCreateInfo->MainWindowProc = TestMainWinProc;
pCreateInfo->lx = 50;
pCreateInfo->ty = 50;
pCreateInfo->rx = 300;
pCreateInfo->by = 480;
pCreateInfo->iBkColor = COLOR_lightwhite;
pCreateInfo->dwAddData1 = 0;
pCreateInfo->dwAddData2 = 0;
}
void* TestWindowMain(void* data)
{
MSG Msg;
MAINWINCREATE CreateInfo;
HWND hMainWnd;
InitCreateInfo(&CreateInfo);
if( !(hMainWnd = CreateMainWindow(&CreateInfo)) )
return NULL;
ShowWindow(hMainWnd, SW_SHOWNORMAL);
while( GetMessage(&Msg, hMainWnd) ) {
DispatchMessage(&Msg);
}
MainWindowThreadCleanup(hMainWnd);
return NULL;
}
在上面的代码段中,该线程首先调用 CreateMainWindow建立了主窗口,然后调用 ShowWindow显示了主窗口,最后启动了消息循环。当消息循环因为接收到 MSG_QUIT 消息而终止时,该函数调用了 MainWindowThreadCleanup 清除了相关的线程数据。
从上述代码中可看出主函数不支持窗口类,在调用 CreateMainWindow 函数时直接指定主窗口的窗口过程地址。我们也可以从中看到主窗口所支持的其他属性:
1.窗口风格。表 2.1 给出了所支持的窗口风格
表 2.1 MiniGUI 支持的主窗口风格
风格 |
描述 |
WS_BORDER |
创建一个具有单线边框的窗口 |
WS_THICKFRAME |
创建一个具有宽边框的窗口 |
WS_THINFRAME |
创建一个具有细边框的窗口 |
WS_CAPTION |
创建一个具有标题栏的窗口 |
WS_HSCROLL |
创建一个具有水平滚动条的窗口 |
WS_MAXMIZEBOX |
创建一个具有最大化框的窗口 |
WS_MINIMIZEBOX |
创建一个具有最小化框的窗口 |
WS_SYSMENU |
创建一个具有系统菜单的窗口 |
WS_VSCROLL |
创建一个具有垂直滚动条的窗口 |
WS_DISABLED |
创建一个初始为禁止的窗口 |
WS_MAXIMIZE |
创建一个初始最大化的窗口 |
WS_MINIMIZE |
创建一个初始最小化的窗口 |
WS_VISIBLE |
创建一个初始可见的窗口 |
WS_EX_TOPMOST |
创建一个顶层窗口,这是一个 Win32 的扩展风格 |
2.窗口标题。
3.窗口菜单。
4.窗口图标。
5.窗口背景色。