|
第十四章 设计Shell集成应用
有一些工具可以使应用程序更紧密地与Shell和底层系统进行集成。也就是说,用户可以象处理系统文档和程序那样处理你的文档和程序。例如,右击文件来显示可用功能列表等。Windows为每一个文件提供默认的功能集,如‘打开…’,‘属性’,‘拷贝’等。是否能为特定的文档增加特殊功能。为此,我们必须客户化这个文档类的关联菜单。
另一个应该与Shell集成的例子是:假设你的程序有建立空文档的能力,用户使用系统的‘新建’菜单项在任一文件夹上飞快地建立新文档,要想如此,就必须在系统注册表中记入一些信息。
当然,这是特殊情形,作为开发人员和应用设计人员还应该重视许多其它有用的特征。在这一章中我们将讨论Shell集成的各方面技术,以帮助开发者将应用与系统Shell无缝集成,使你的产品更专业。这些技术包括:
怎样客户化关联菜单
怎样注册新文件类型
怎样设计和编程处理命令行
怎样编程定制‘打开’对话框
我们将设计一个基于文档的全属性应用,这个应用显示和打印所有Windows支持的元文件,从传统的(*.wmf)到增强的(*.emf)。我们将应用所有我们前面讨论过的理论技术,以及Shell对元文件的丰富支持。
Shell集成应用
头一个问题显然是Shell集成应用的组成结构是怎样的。就Win32的面向文档程序概念,有几个关系到系统Shell的特征需要给出。简单地说,有三组定义Shell集成应用的特征:
为程序处理的任何文档注册图标和类型名
为程序处理的文档客户化关联菜单
在系统的‘新建’菜单中可能有一个或多个客户化条目
单一程序实例
对每一个打开过的文档在‘最近文档’文件夹中有一个新条目
完全支持长文件名,尤其是对于用户文档。
除了这些基本特征外,我们还需增加一些不常使用的特征:
在系统的‘发送到’菜单中客户化一个或多个条目
在‘开始’和‘程序’菜单中客户化一个或多个条目
在‘Favorites’文件夹中客户化一个或多个项
在桌面上生成一个或多个新快捷方式
有一个应用桌面工具条来集中程序的所有主要功能
客户化某些系统公共对话框
在用户下一次登录时注册应用自动启动
第三组特征主要受限于特定安装器,如InstallShield 和WISE,它们是:
拷贝共享文件到系统公共路径
在‘程序文件’文件夹下安装应用
提供卸载程序
探测Shell的应用路径名来定义查找文件的路径
这些要求来自于Windows标记规范,然而对于Windows环境下更高层次的抽象概念而言它们是最基础的:为了打开和使用文档,用户不必知道实际装入和显示文档的是什么程序,只需找到和双击文档的图标和这个文档的名字即可(实际在桌面设置中也可以设置单机打开方式)。
文档和Shell
在Windows95发布以后,文档成为系统Shell更中心的角色,文档已经成为了主角,而实际处理它的程序则缩减成配角。甚至它们在硬盘驱动器上的位置都标明其状态的低下:程序被分组在‘程序文件’文件夹下,每一个都有自己的子文件夹,以及一个存储DLL和其它帮助文件的子目录。这样的许多文件夹都是隐藏的——这进一步确认程序相对于文档是次要的。

查看上面的截图,我们可以看到文档有它们自己的图标和描述,更进一步,每一个文档都有专门的关联菜单,从菜单中可以执行一些Shell功能。有些功能可以应用于各种文档,因而出现在所有文档的关联菜单中,有些则是个别文档类型所具有的。
基本的文档功能
Windows Shell提供一些菜单动词,可以自由使用,它们是:
拷贝、剪切、粘贴
删除
重命名
建立快捷方式
属性
此外,还有两个菜单命令,‘打开’或‘打开方式…’,但是这两个是相互排斥的——后者仅仅在没有注册程序打开指定文档时出现,并且导出下面的对话框:

相反‘打开’命令则依赖于存储在注册表中的条目内容,这就是我们将要讨论的。
‘发送到’命令
另一个我们总能看到的命令是‘发送到’,它有一个子菜单。这个子菜单列出了选中文档可能的目的地。‘目的地’是一个在命令行上接收给定文件名的程序。下图说明‘发送到’菜单是怎样把文件设置成新邮件的附件:

通过我们在这一节中列出的命令,Shell保证了对PC上各种文档的最低层支持。对于有经验的用户和软件工程师,这就等于提供了使用更多的文档特定特征扩展这些基本行为的机会。
注册文档类型
关于Shell构造的所有信息都存储在系统注册表中,所以修改Shell表现或行为的任何方法都必须通过注册表。
为了使Shell识别和适当处理一定种类的文档,它就必须是一个注册类型。一个文档的类型由它的文件名的扩展所标识,而且所有注册的文档类型都存储在HKEY_CLASSES_ROOT注册表节点下:

文件扩展(.ext)的条目指向同一个节点下的另一个键,其名字存储在.ext的默认值中,在上图中,对EML文件(微软邮件消息,OutlookExpress格式文件),其值为:
Microsoft Internet Mail Message
如果需要获取这个文档类型的注册信息,你就必须探索:
HKEY_CLASSES_ROOT
/Microsoft Internet Mail Message

在这个键下,存储了应用于三个方面的信息:
用户接口
关联菜单
Shell扩展
文档的Shell用户接口
从这一节的标题可以看出,我们主要是想说明设置文档的图像属性——即图标和类型名。‘DefaultIcon’键使你可以指派图标到特定类型的所有文件。这个键的Default值包含一个如下格式的字符串:
C:/PROGRAMS/THEPROG.EXE,0
再次注意,这个信息不是存储在.ext键下的,而是在.ext指向的键下。
这个串标识默认图标由全路径名、逗号和索引号构成。要显示的图标是给定文件中指定索引号的图标——记住图标索引总是从0开始的。如果索引是负数,则表示是一个资源ID,比如对于EML,DefaultIcon串为:
C:/PROGRAM FILES/OUTLOOK EXPRESS/MSIMN.EXE,-4
正象上面显示的,主键(上面示例中是Microsoft Internet Mail Message)的默认值包含了用于这个文档类型名的串。要修改这个设置并不需要专门的Windows程序员进行,任何老练的Windows用户都可以改变EML文件的描述,或表示的图标。然而要编程地插入键来注册文档完全不同于手动修改注册表操作。我们需要集中讨论软件自动集成其文档与Shell的操作关系。
关联菜单的特定文档命令
命名为Shell的键可以包含一定数量的子键,每一个子键都对应一个特殊的命令,这是将要显示在文档关联菜单上的。在Shell键下的这些键称为动词(此时的动词是‘打开’),这些键的默认值包含了将要显示在关联菜单上的串。如果没有设置,则菜单显示键本身的名字。因此动词打开可以在菜单中显示一个相当不同的名字。在我们讨论的邮件消息这个例子中,‘Read’命令在菜单中替代了‘打开’。如果你改变了默认值的内容并且调出关联菜单,你将看到这个结果:

关联菜单显示的是‘读’,而实际行为一点也没改变,因为这是在命令子键的默认值中确立的。每一个动词必须有一个命令子键包含可执行文件路径和命令行,以及任何其它必要的设置。指定命令行以及适当的开关,用%1表示要操作的文件也是十分重要的:
C:/PROGRAM FILES/OUTLOOK EXPRESS/MSIMN.EXE" /eml:%1
上面这行显示EML文件的命令行——在你的机器上是否有相同的设置依赖于Outlook Express的安装设置。
Shell对文档的扩展
通过修改注册表你可以添加静态动词到文档的关联菜单。你所定义的任何静态动词总是被显示,并且总是执行同样的命令行。
更灵活的动态的行为可以通过Shell扩展获得,我们将在下一章中进行讨论。现在,我们可以说Shell扩展是运行在探测器地址空间中的一段代码,每次探测器需要做某个客户化行为时都调用这段代码,如绘制图标、显示关联菜单等。你的代码段给出一个动态确定菜单项添加的机会和响应用户的点击的操作。
对于给定文档类的所有Shell扩展,都列出在shellex键下,它与Shell键同层。
怎样影响程序
我们现在接触到了一定数量的影响文档的属性,并且在这一章的开始我们就说明了文档是Windows Shell的重点。然而,我们并没有脱离文档仍然通过程序显示这样一个事实——问题是,通过我们致力的Shell集成怎样和什么程度上影响到程序。
有两个重点,首先是,用户可以点击重复打开不同的文档,甚或是同一个文档的多个拷贝。当这种情况发生时,程序被重复调用,所以,为了避免窗口增殖,你可能希望只允许运行一个程序实例。其次是,程序命令行的重要性,因为静态动词通常是通过命令行的开关实现的。你应该以非常标准的方法输出最重要的功能。
在第11章中我们讨论了RunDLL32模块,它给出了通过命令行调用具有固定原型的动态库函数的好方法。在这两种情形下程序的功能都必须明显地隔离开,而且可容易地从外部模块调用。
当某人在Windows Shell下点击文档时,程序被调用,程序每次启动,都检查是否有运行中的副本,如果有,则传递控制和命令行,而当前的实例则退出,我们将在后面进一步讨论这个问题。
MDI 与 SDI
MDI和SDI是Windows基于文件应用的两个典型设计。MDI表示多文档界面,说明程序可以同时打开几个文档,每一个都有自己的窗口。相反,SDI是单文档界面的首字母缩写——SDI程序每次仅能打开一个文档。传统上主要的Windows应用都是MDI形式的——Office套件是一个典型的MDI例子,而记事本和图画程序则是SDI的例子。
从Shell的观点上看,选择MDI或SDI并不是实际的结果。然而在进一步的探索后,你可能会认识到对MDI和SDI之间差异的讨论实际是要在一个更宽泛的对比上打开窗口:应用为中心对比文档为中心的环境。
MDI方案由应用来支配,是应用打开和管理各个子文档。反之SDI界面是更加文档为中心的:你查看由可用工具环绕的单一文档,可以使用这些工具来修改它。
自从Windows95发布以后微软就开始推荐尽可能使用SDI开发应用,但是有许多人都对这个建议不感冒。
建立新文档
在任何时候你在探测器显示文件夹内容的窗口上右击,都会有下图样式的菜单出现:

‘New’命令列出了所有文档类型,这些是可以经由Shell建立的文档类型。当你选择了一个列出的文档类型后,Shell调用注册的应用,并请求它来建立一个新的文档,其名字是一个来自文档类型名(与它在菜单中的相同),前缀有一个‘New’字。例如,要建立一个新的bitmap图像文档,文件名默认为:
New Bitmap Image.bmp
New菜单
出现在‘New’菜单中的每一项(除了文件夹和快捷方式)都有相关的文件类对应的ShellNew键存在,它在下面的注册表路径上:
HKEY_CLASSES_ROOT
/.ext
/ShellNew
ShellNew键的内容确定了New菜单上显示的内容,以及当点击时所需要做的动作。实际上有四种通过Shell建立新文档的方法,你可以建立:
空,零长度文档
从默认文档拷贝的文档
来自注册表存储的二进制数据的文档
由特殊外部程序建立的文档,例如建立大师软件
很自然这些选择要求不同的注册表设置:
值
内容
NullFile
空字符串
FileName
作为模板使用的文件名。这是假设这种文件是驻留在Windows/ShellNew目录下的。
Data
一块从注册表读出的二进制数据
Command
建立文档所需要的命令行
下面图像显示对机器上的BMP文件的设置:

一般使用NULLFile设置,令应用处理空或零长度文件。FileName设置与Word和Excel密切相关,如果使用复杂的、混合文件作为文件所需要的最小结构,即使是空文件,这个设置是有用的。此时,你可以准备一个标准文件(空的或不空的),把它保存到FileNew值中,每次建立这种类型的新文件时,就建立这个文件的一个拷贝。Data是可以包含二进制数据的值,它们可以填充到新建立的文件中。这与FileNew的情况稍有不同,在FileNew中,模板是单独文件,而Data,是一块存储在注册表中的数据。
在第11章讨论置换快建立捷方式标准处理器时我们遇到过Command值。如果给出这个值,将限制Shell运行指定的命令行,并断定它将建立指定类型的新文档。这个选项由逐步建立文档的大师程序所特殊设定。
下面我们将考察一个例子,其作用是添加一个命令到Shell的‘New’菜单,建立一个新的具有最小内容的HTML文件。
建立HTML新文件
我们假设在PC上注册了一个处理HTML文件的程序。当需要从腹稿建立新的HTML文档时,通常是:
运行可视HTML编辑器(例如微软的FrontPage)
运行记事本或其它普通的文字编辑器
像绝大多数其它Windows文档一样,在你需要编写HTML页面时,需要借助‘记事本’。然而,HTML文件不是ASCII文件,它需要有标记来描述使它成为有效的浏览器可处理的文档。最小HTML文件可以有如下形式:
<html>
<body>
</body>
</html>
保存这段代码到一个命名为html4.htm的文件,并把它放置在Windows/ShellNew(或Winnt/ShellNew)目录中。然后打开注册表编辑器,添加ShellNew键到:
HKEY_CLASSES_ROOT
/.htm
下。这个新建立的键也必须给定FileName串值:

保存了这些设置后,你就能右击桌面,激活菜单:

这个图说明在PC上改变了htmlfile注册表键的初始描述为‘Web Page’之后任何从这个菜单项建立的新文件都将调用‘New Web Page.htm’。
注意:只有在文件类型被正确地注册之后,你才可以添加项目到‘New’菜单项。
其它特征
要设计和编写良好的Shell集成应用,还有两个特征需要注意。它们是:
存储目录列表到辅助模块如DLL可以找到的位置
安排在下一次登录时自动运行
第一个特征可能与设置程序有更大的关联,但是并不是所有安装器都确实完成你所要求的任务,因此需要你自己编写功能扩展,以及深入专研注册表路径。第二个特征典型是探测器和其他几个应用的特征。在关闭系统时,如果应用仍然在运行,Shell将在下一次登录时自动重新启动它。现在就让我们看一下怎样编码这个行为。
应用路径
几乎所有的Windows应用都由一个或多个文件组成。典型地是有一个EXE文件和多个DLL(不是提及的系统Dlls,如kernel32.dll和user32.dll)。辅助的DLL必须由安装器拷贝到某个地方。它们可以在‘程序’文件夹或其它地方,但是,微软反对将DLL拷贝到系统主文件夹下,如Windows或Windows/System等。如果你确定不将DLL放到与EXE文件相同的目录下,很快你就会获得系统通知的错误信息,说明系统不能定位指定的DLL。
决定不把DLL放置到与EXE相同的文件夹下是为什么。因为你的应用可以是一套共享辅助DLL的很多程序的一部分,如果每一部分都备份这些DLL,显然是一种浪费,所以,你可以建立一个公共文件夹,放置可共享的每一件东西到这个文件夹。现在的问题是怎样使Shell知道它的存在——当你导出需要确定的库的应用时,你必须保证这个库的路径是全程可视的。
MS-DOS基础程序(Windows程序也一样)依赖于PATH环境变量。类似地,对于Shell把这个置换成应用路径。要使用这个概念,在安装了应用之后,需要添加下面的注册表键(比如对于Program.exe):
HKEY_LOCAL_MACHINE
/SOFTWARE
/Microsoft
/Windows
/CurrentVersion
/App Paths
/Program.exe
这个键的默认值包含可执行文件的全路径名,如果给出,则路径值列出所有可以查找其它文件的路径:

应用的自动启动
当一个特殊用户登录时,Windows将努力读出下面的键:
HKEY_CURRENT_USER
/Software
/Microsoft
/Windows
/CurrentVersion
/RunOnce
如果这个键存在,名字被存储在键值中的任何程序都将被执行。在检查过之后所有条目都将被删除。所以,它们仅被执行一次。据此,我们可以编码使应用能够在下一次特殊用户登录时执行。注意,这仅仅是在下一次登陆时,不是每一次持续登陆时。要每一次特殊用户登录都运行应用有一个注册表条目在上面位置的Run键下。自动执行不完全是系统特征,程序必须协同操作。特别是,程序添加其本身(或任何其它应用)到RunOne键下,正确地作这个操作就是响应WM_ENDSESSION消息。当然,在任何时候都可以做,然而因为我们的目标是获得持续的交互会话,所以,只有在关闭当前会话后我们的程序仍然在运行时才建立这个条目。这也WM_ENDSESSION消息到达时。关于应用运行的信息在注册表中有如下格式:
ID = program name
你需要建立一个值,其内容是可执行程序的路径名。ID必须是唯一的,但是与你指定什么名字并不重要。下图显示一个例子:

这些条目以它们键入的顺序依次取出——这个顺序不一定符合注册表编辑器的输出顺序,在编辑器中条目是按字母顺序显示的。程序是一个接一个异步导出的,如果你有上面图中显示的注册表条目设置,你就会发现,当你登录时记事本程序在桌面上打开。
另一个RunOnce键
还有另一个比RunOnce更强的RunOnce键在下面的路径下:
HKEY_LOCAL_MACHINE
/SOFTWARE
/Microsoft
/Windows
/CurrentVersion
/RunOnce
使用这个键的语法实际与前面介绍的相同,但是,其工作方法有三方面不同,它们是:
任何用户登录,这个键的内容都被检查
各个注册程序同步执行——仅在前一程序完成之后,下一条目开始运行。
在这个键下注册的程序运行在HKEY_CURRENT_USER节点相同子键注册的程序之前。
如果你在HKEY_LOCAL_MACHINE/.../RunOnce键下注册了记事本程序,下一次登录或重启动时记事本将在任务条和桌面图标之前出现在桌面上。更要紧的是你不能看到桌面和图标,直道关闭这个窗口终止过程之后才可以。
运行键
Run键,我们在上面讨论中临时提及的键,也是在HKEY_LOCAL_MACHINE和HKEY_CURRENT_USER
下都存在的键。Run和RunOnce除了在最后删除所读出的注册条目外,它们有一致的逻辑。在Run下的项目每次用户登录时都执行。
RunServices键
在Windows95和Windows98下有两个键允许你模仿NT服务——即,在用户登录之前运行模块。它们是:
HKEY_LOCAL_MACHINE
/SOFTWARE
/Microsoft
/Windows
/CurrentVersion
/RunServices
/RunServicesOnce
它们的语法与我们前面讨论过的其它键的语法相同。RunService在每次登录之前运行应用,而RunServiceOnce则与下一次登陆作相同的操作。执行程序是异步的,在用户实际登录之后可以终止。在任何场合,比照RunOnce和Run键,所有服务都必须在系统开始之前执行,
下面的表给出启动期间,Windows操作注册表键的实际顺序:
步
键
1
HKLM/.../RunServicesOnce ( NT下不支持)
2
HKLM/.../RunServices (NT下不支持)
3
用户登录。用户可以在所有服务开始之前登录
4
所有服务开始和用户登录
5
HKLM/.../RunOnce
6
所有注册程序完成
7
HKLM/.../Run
8
HKCU/.../Run
9
当前用户包含在Startup文件夹下的程序
10
HKCU/.../RunOnce
Winlogon键
如果在任何用户登录之前,仅需要简单地显示信息,你可以探索下面键的注册条目:
HKEY_LOCAL_MACHINE
/SOFTWARE
/Microsoft
|
|