Step 1:使用 MiniGUI 创建窗口
首先回答一个问题:什么是 MiniGUI。简而言之,对用户来说,MiniGUI 就是在控制台上为用户提供一个图形接口,使用户能够利用它提供的函数创建图形界面程序。MiniGUI 占用非常少的系统资源,适用于嵌入式系统应用程序。
出于历史的原因,在开发 MiniGUI 的时候,模拟了 Windows 系统的某些机制,因此如果用户非常了解 Windows 应用程序的开发,则在使用 MiniGUI 的时候不会遇到任何困难。如果用户对 Windows 开发还处于一无所知的程度,那么最好先找一本 Windows 开发的参考资料(并非 MFC),只需要看完消息循环部分就足够了。因此在本文中,将对 MiniGUI 的运行机制不做过多的论述。
      先来回顾一下 Windows 应用程序编制的过程:
1. 在 WinMain() 中创建窗口,使用以下步骤:创建窗口类、登记窗口类、创建并显示窗口、启动消息循环机制。
2. 在 WndProc() 中,负责对发到窗口中的各种消息进行响应。
在 MiniGUI 中也同样要有这两个函数,并且步骤与函数基本与 Windows 开发一致。请进入 step1 目录,看 step1.c 文件。
在 MiniGUI 中,窗口主函数名字叫MiniGUIMain(),它负责创建程序的主窗口。在这个过程中,MiniGUI 使用MAINWINCREATE 结构把 Windows 中的创建窗口类和创建窗口风格合二为一。该结构中元素的含义是:
CreateInfo.dwStyle: 窗口风格
CreateInfo.spCaption: 窗口的标题
CreateInfo.dwExStyle : 窗口的附加风格
CreateInfo.hMenu: 附加在窗口上的菜单句柄
CreateInfo.hCursor: 在窗口中所使用的光标句柄
CreateInfo.hIcon: 程序的图标
CreateInfo.MainWindowProc: 该窗口的消息处理函数指针
CreateInfo.lx: 窗口左上角相对屏幕的绝对横坐标,以象素点表示
CreateInfo.ty: 窗口左上角相对屏幕的绝对纵坐标,以象素点表示
CreateInfo.rx: 窗口的长,以象素点表示
CreateInfo.by: 窗口的高,以象素点表示
CreateInfo.iBkColor: 窗口背景颜色
CreateInfo.dwAddData: 附带给窗口的一个 32 位值
CreateInfo.hHosting: 窗口消息队列所属
其中有两点要特别说明:
1. CreateInfo.dwAddData:在程序编制过程中,应该尽量减少静态变量,但是如何不使用静态变量而给窗口传递参数呢?这时可以使用这个域。该域是一个 32 位的值,因此可以把所有需要传递给窗口的参数编制成一个结构,而将结构的指针赋予该域。在窗口过程中,可以使用、函数获取该指针,从而获得所需要传递的参数。
2. CreateInfo.hHosting:该域中的值表示窗口使用哪一个消息队列。系统中维护了多个消息队列,那么系统如何知道发给该窗口的消息从哪个消息队列中检索呢?就使用该参数。可以让多个窗口使用同一个消息队列。
后面的过程与 Windows 编程没有任何区别,不再赘述。
MainWinProc 函数负责处理窗口消息。在这个例子中,将在窗口中画一个方框,然后在方框中用三种 True Type 字体写同一句话。请读者注意的是和 Windows 程序一样,字体也是系统资源,当退出应用程序的时候,不要忘记删除它们。
Setp 2:如何使用资源创建菜单和控件
在使用 Windows 下的集成开发环境时,一般都提供一个资源编辑器,可以创建用户自己的菜单、对话框,并且用来布置对话框中控件的位置。最后将编辑好的资源保存在磁盘文件中,一般取名为 app-name.rc。但是在 MiniGUI 中,还没有开发出一个资源编辑器,因此用户不得不手工完成一切操作。目前 MiniGUI 正在做这方面的工作。
有兴趣的用户可以研究一下 Windows 资源文件。它是一个纯文本文件,定义了菜单的各个项目和控件的大小及 ID。MiniGUI 也基本按这个格式和思路来实现的。但是必须提醒读者的是:在 Windows 资源文件中,控件和对话框的大小是以系统字体为单位的,而在 MiniGUI 中,是以屏幕的象素点为单位的。 在 Step2.c 文件中,要告诉读者如何创建自己的控件和菜单,在 Step3 中,再讲述如何创建对话框。
在 Step2.c 文件中,要告诉读者如何创建自己的控件和菜单,在 Step3 中,再讲述如何创建对话框。
2.1 创建菜单
无论在 Windows 中还是在 MiniGUI 中,属于窗口的菜单都是由以下两个元素构成:
1. 主菜单,它包含了菜单所占用的物理位置和其中的菜单项;
2. 子菜单,当点击菜单项时弹出的菜单。
当然子菜单还可以包含子菜单,此时它成为自己子菜单的主菜单了。在 MiniGUI 中就是按照上面的两个组成部分创建菜单的。
static HMENU createmenu ();{HMENU hmnu;MENUITEMINFO mii; hmnu = CreateMenu(); memset (&mii, 0, sizeof(MENUITEMINFO)); mii.type = MFT_STRING; mii.id = 100; mii.typedata = (DWORD)"File"; mii.hsubmenu = createpmenufile (); InsertMenuItem(hmnu, 0, TRUE, &mii); ……… ……… return hmnu;}
首先使用 CreateMenu() 函数创建一个主菜单,然后用MENUITEMINFO 结构创建每一个菜单项。
MENUITEMINFO 是一个比较复杂的结构,其中用户经常用到的有如下几项:
mii.type: 菜单类型。常用的有MFT_STRING、MFT_BITMAP、MFT_SEPARATOR
mii.state: 菜单状态。0 表示激活,1 表示禁止
mii.id: 菜单 ID
mii.hsubmenu: 该菜单项对应子菜单句柄
mii.typedata: 显示的菜单项字符
当定义完各个域后,用 InsertMenuItem()函数将菜单项插入。该函数的第一个参数指的是要插入的菜单句柄;第二个参数是该菜单项的序号;第三个参数是一个标志;第四个参数是MENUITEMINFO 结构指针。
而各个子菜单的创建过程与此类似,不同点在于:子菜单使用CreatePopupMenu (&mii) 函数创建,并且附带有一个MENUITEMINFO 结构指针的参数,指明菜单的第一项是什么。有兴趣的读者可以试着创建第二层子菜单。
2.2 创建控件
在窗口中创建控件比创建菜单更加容易。只要响应 MSG_CREATE 函数即可。
控件一律使用 CreateWindow 函数创建。该函数的各个参数含义是:该控件的类型;控件上显示的字符;控件的风格;控件相对于所在窗口左上角的 x 坐标;控件相对于所在窗口左上角的 y 坐标;控件的宽;控件的高;父窗口句柄;附带的 32 位数据。创建完毕后,使用 ShowWindow() 函数显示。
读者可能对于 MiniGUI 所提供函数的各个参数还不甚了解,请参照 MiniGUI 源代码中的 include/window.h。
Step3:如何创建对话框,使用消息循环
3.1 消息循环
如同 Windows 系统一样,MiniGUI 也是*消息循环机制来完成各个作业的。当触发一个窗口消息后,系统将调用该窗口的消息处理函数来响应。step3.c 告诉读者是如何响应窗口控件消息和菜单消息的。
当点击菜单或者控件时,它们都向所在的父窗口发送 MSG_COMMAND 消息,其中消息结构中的wParam 参数指定的是该消息来自哪个控件或菜单项。在 step3.c 中,当点击按钮时,消息处理函数将调用InvalidateRect()函数,该函数的作用是向 hwnd 指定的窗口发送 MSG_PAINT 消息,导致窗口重绘。
在 MSG_PAINT 消息处理中,将根据标志显示一副图片。
在响应菜单消息时,将弹出一个对话框。
3.2 制作对话框
对话框是这样做的:
1. 创建对话框模板的各个域含义是:
DWORD dwStyle; // 对话框风格
DWORD dwExStyle; // 对话框附加风格
int x, y, w, h; // 对话框位置和宽高,以象素点为单位
const char* caption; // 对话框题目
HICON hIcon; // 对话框图标句柄
HMENU hMenu; // 对话框菜单句柄
int controlnr; // 对话框内控件的数目
PCTRLDATA controls; // 控件数组
DWORD dwAddData; // 对话框附带的 32 位数据
2. 创建对话框内的控件数组:对话框内的控件的位置、类型和风格都由数组中的一个元素定义。
CTRLDATA 结构定义是:
char* class_name; // 控件类
DWORD dwStyle; // 控件风格
int x, y, w, h; // 控件位置和宽高,以象素点为单位
int id; // 控件 ID
const char* caption; // 控件上的文字
DWORD dwAddData; // 附带的 32 位数据
3. 创建窗口过程:与创建主窗口过程类似,不再赘述。
4. 响应消息,显示对话框:
DlgInitProgress.controls = CtrlInitProgress; // 给对话框模板的控件数组域赋值
DialogBoxIndirectParam (&DlgInitProgress, hWnd, DialogBoxProc, 0L); // 参数是:对话框模板、
父窗口、窗口处理过程、附加 32 位 数据。
经过以上过程,就可以显示出对话框了。