由于GUI Shell是用户启动其他GUI应用程序的基础,因此在GUI模块初始化的时候,就必须启动shell,否则用户将无法与计算机交互。前面介绍过,GUI模块被加载到内存后,操作系统会创建一个叫做“GUI”(注意不是GUIShell)的线程,然后等待这个线程的结束。GUI线程被调度执行后,就会以__init函数作为入口函数开始GUI功能的初始化。为了加深读者印象,再把__init函数的部分相关代码摘录如下:
__init函数首先初始化显示硬件,初始化全局对象等,这在前面已讲解,不再赘述。接着,初始化函数会创建原始输入线程(RAWIT),并设置为当前输入焦点线程。自此开始,所有用户主动输入,都会被送到GUI模块。但这时候由于系统中还没有创建另外的GUI线程,任何消息都会被RAWIT线程丢弃。严格来说,是RAWIT线程试图搜索窗口树,由于这时候的窗口树是一棵空树,因此不会搜索到任何目标线程,从而导致消息被丢弃。
接下来,初始化函数创建了GUIShell线程,也就是图形用户shell线程。这个线程的入口点是GuiShellEntry函数。创建完毕,GUI线程就进入等待状态,等待RAWIT和GUIShell运行完毕。需要注意的是,这时候的RAWIT和GUIShell线程仅仅是被创建完成了,还不一定会被调度运行。因为若系统中存在更高优先级的核心线程且状态为READY的话,那么这两个线程会一直处于就绪状态,不会得到调度。
GUIShell线程一旦得到调度,就会从GuiShellEntry函数处开始执行。下面就是GuiShellEntry的入口代码:
这段代码看起来是不是很熟悉?这是一个典型的基于消息驱动的线程入口函数。该函数首先调用InitGuiShell函数,做了一些初始化工作,然后进入一个消息循环。在这个消息循环中,处理了三个典型消息:定时器消息、窗口消息和终止消息。与字符模式的shell不同,GUI Shell是可退出的,在图形模式下,用户按下Ctrl+Alt+Del组合键,即可退出图形模式。终止消息(KERNEL_MESSAGE_TERMINAL)就是用于通知GUI Shell,用户已经发起了退出操作。这个消息是键盘驱动程序根据用户输入自动生成的,即如果用户连续按下了Ctrl、Alt和Del组合键,键盘驱动程序不但会给操作系统内核发送三个键盘按下消息,分别对应Ctrl、Alt和Del,还会额外发送一个TERMINAL消息。DIM不会对这个消息做进一步处理,只会把这个消息透传给当前输入焦点线程。此时的当前焦点线程是RAWIT,于是RAWIT会通知GUI Shell启动退出操作。详细的传递机制,在本章后续章节中会有详细介绍。
定时器消息和窗口消息的处理机制,在前面相关章节中已介绍过,不再赘述。总之,GUI Shell本质上是一个基于GUI接口的用户线程,这个线程受消息驱动。与普通应用程序不同的是,GUI Shell是GUI模式下的第一个用户线程。
所有GUI Shell的初始化工作,是在InitGuiShell函数内完成的。这个函数创建了GUI Shell的所有窗口(应用程序显示窗口、系统信息显示窗口、辅助信息显示窗口等)。为了简便,我们摘取该函数的部分代码如下:
上述代码片断创建了管理应用程序的窗口,即GUI模式下屏幕左边用于呈现所有已安装应用程序的窗口。其他窗口,比如辅助信息窗口(呈现了时钟和日历)、系统信息显示窗口等,都是在该函数内完成创建的。
应用程序管理窗口的窗口函数就是MainFrameProc,这是要重点介绍的窗口函数。在窗口创建完成时,系统会给应用程序管理窗口发送WM_CREATE消息,即以WM_CREATE作为参数,调用MainFrameProc函数。在MainFrameProc函数的WM_CREATE消息处理代码中,加载了系统中的所有应用程序。下面是其代码:(www.xing528.com)
这个函数结构比较简单,是一个典型的窗口函数的结构。针对每个窗口消息,该函数又调用了其他的函数来完成具体功能。比如针对WM_CREATE消息,该函数调用LoadApp Profile,来完成所有应用程序的枚举工作。这里“应用程序的枚举”,实际上是遍历HCGUIAPP目录,把该目录下所有扩展名是HCX的文件检查一遍,如果确认是一个合法的可执行文件,则提取其相关信息,形成一个application profile,然后把这个profile显示在屏幕上。这里所说的应用程序profile,指的是能够描述应用程序基本信息的一个数据结构,包含了应用程序文件名、应用程序可视化名字、应用程序文件大小、应用程序图标等。这些信息用于加载一个特定的应用程序。LoadAppProfile的实现代码比较繁琐,在此就不列举了,只把该函数的大致执行过程描述一下,读者可根据下面的描述自行阅读该函数的代码(位于[gui/kthread/launch.cpp]):
(1)该函数首先检查应用程序目录下(C:\HCGUIAPP)所有已安装的GUI应用程序。在检查的时候,该函数调用了FindFirstFile和FindNextFile等目录枚举函数。针对每个扩展名是.HCX(或对应的小写形式)的文件,该函数读取该文件的开始部分信息,然后做进一步检查。如果发现是一个合法的HCX应用程序,则会进一步读取应用程序文件,提取应用程序图片、可视化名字等信息,然后保存在一个列表中。
(2)在枚举完应用程序信息后,就形成了一个应用程序profile列表(代码中的pAppProfile变量),这个列表包含了所有安装在应用程序目录下的合法应用程序。针对每一个应用程序profile,该函数调用CreateBitmapButton,在窗口内创建一个图形按钮。这个图形按钮显示应用程序图标和应用程序可视化名字两个信息。
(3)所有应用程序profile的图形按钮创建完毕,该函数就执行完了。
图形按钮(bitmap button)是V1.75实现的一个简单的用户交互控件。与普通的按钮控件不同的是,该控件可以显示一个bitmap格式的位图信息。一旦用户单击了按钮,按钮会给其父窗口发送WM_COMMAND消息。WM_COMMAND消息的wParam参数携带了一个ID,这个ID标明了图形按钮的一个标识。回过去看MainFrameProc函数的代码,该函数处理了WM_COMMAND消息,这个消息就是由图形按钮发送过去的。
在处理图形按钮通知的WM_COMMAND消息时,调用了LaunchApplication函数。顾名思义,这个函数就是根据图形按钮的索引ID,启动其对应的应用程序。这是本章11.9.3节的内容。
至此,GUI Shell初始化这个场景就介绍完了。从本质上说,GUI Shell是一个用户线程,其特别之处在于,它是GUI模块初始化后的第一个用户线程。与其他基于GUI的线程相通,它以消息为驱动机制,根据消息调用不同的窗口函数,从而实现不同的功能。以线程机制为基础,把一个复杂的功能,划分为一个个独立的模块(线程)分别实现,并以消息作为模块(线程)之间的交互机制,是一种非常灵活且富有弹性的软件开发方式。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。