访问注册表--通过VJ6来处理Windows注册表
Microsoft Visual J++ 6.0(VJ6)向Java开发人员提供了Windows API的调用,其中包含了Windows结构中的重要组件,比如系统的注册表。
我们都知道Java的目的是为了实现跨平台的应用程序开发,因此我们不必惊讶Java语言不支持系统的注册表和其它Windows相关的特性。有一些Java开发者强烈反对VJ6引入的新的Java 语言的扩展,而其它的人则不关心跨平台的开发,他们想编写Windows相关的应用程序,使用一种现代的、功能强大的、面向对象的语言--Java。
系统注册表是Windows 95、Windows 98和Windows NT结构的一个重要部分,注册表的真正强大功能--从一个应用程序开发者的角度来说--就是能够存储您的应用程序相关的信息。如果您想要使您的应用程序对用户友好,一个方法是在应用程序退出时把设置保存起来。这样,一些象窗口位置、缺省数据库和最近访问的文件等设置将在下次用户载入应用程序时成为缺省设置。
本文将简要地讨论系统注册表的基础,还将介绍允许您方便地访问系统注册表的VJ6扩展。举例的应用程序使用VJ6用户界面的另一个新特性--Windows基本类库(WFC)。
注册表
有经验的开发人员都知道系统注册表,如果你犯了一个错误删除或者修改了错误的选项,你可能不得不重新安装Windows。如果你在修改注册表之前知道你想要做什么,你就可以安全地使用注册表了。最安全的方法是在你改变任何注册表选项之前先备份系统注册表,这样如果你不小心犯了一个错误修改或者删除了错误的选项的话也不会出现灾难性的后果。
[
object manager]
options = 1,1,1,1,1,1,1,2,1,0,1
connect on startup = 0
[Reports]
ReportDefFileName = C:\PROGRA-1\LOGICW-1\ERWIN3-1.5\reports.erp
ReportFontSize = -11
[Recent Template List]
Template1 = C:\PROGRA-1\LOGICW-1\ERWIN3-1.5\TEMPLATE\Dimens.ert
Template2 = C:\PROGRA-1\LOGICW-1\ERWIN3-1.5\TEMPLATE\Sample.ert
Template3 = C:\PROGRA-1\LOGICW-1\ERWIN3-1.5\TEMPLATE\Progress.ert
图一:一个.INI文件示例
一些开发人员完全避免使用注册表,他们把配置信息保存在.INI文件中。如果你曾经在Windows 3.1或者更早的版本中开发应用程序,你会记得配置信息是保存在文本文件中,通常在\Windows子目录中,带有一个.INI的文件扩展名。一些开发人员争论说,如果仍然可以使用.INI文件,为什么要转而使用注册表呢?
使用注册表的原因有很多,注册表比.INI文件更加灵活。.INI文件是更加原始的文本文件,你存储于其中的任何值都必须转换成一个文本字符串。而注册表就不一样了,因为它是一个二进制的数据库,你不需要转换就可以直接在其中存储许多数据类型。注册表的最大优势是它只有一个,也就是说它把所有Windows应用程序在从一个会话切换到另一个会话时必须保持一致的所有数据存放在一个集中的地点。这和.INI类型文件的系统有着显著的不同,在.INI文件类型的系统中,所有的.INI文件散布在目录树中。
如果你的应用程序不在一个.INI文件中存储配置信息,你仍然需要对注册表以及如何读取其中的信息非常熟悉,因为几乎所有Windows 95、Windows 98和Windows NT的商业软件和硬件都和操作系统一样在注册表中存储信息。通过学习注册表中的选项,你可以学到许多关于Windows结构以及不同应用程序如何工作的知识。此外,注册表还帮助应用程序之间的协同,如果你忽略了注册表,其它的应用程序将不能访问你的软件。
在注册表中存储信息的另一个优势就是你的程序可以在每一个用户的基础上存储信息。用户可以配置他们自己的应用程序设置,Windows注册表自动地为每一个用户存储和获取设置信息。你不需要强迫每一个用户使用同样的设置,或者为管理不同的用户设置而设计复杂的计划。 Windows注册表为你处理了所有的细节信息。
VJ6使得访问注册表变得相对简单,那些从Visual Basic(VB)转向VJ++的应用程序开发人员将发现运用注册表的强大功能将变得十分容易。VB能够让你获得注册表中的基本特性并访问注册表中绝大多数的信息,但是如果你的要求十分复杂的话,你仍然需要使用Windows API。VB通过限制可以利用的特性把开发人员从复杂性中解放出来。而VJ++着让你处于一种比较危险的处境中。
注册表--就和老的.INI文件相同--用名字-数值对的方式存储信息。例如,在图一中的示例.INI文件中列出了基本的变量,每一个紧接着一个等于号,然后是一个数值,下面是典型的一行:
connect on Startup=0
每一节的头字节(比如
object manager)被中括号括起来。
注册表在信息管理方面则更加灵活,它允许你在一个字段中嵌套一个字段。根键位于最高级别。
Regedit是一个你可以用来直接编辑注册表的小工具(Regedit.exe在\Windows或者\Winnt目录中)。在根键的下面有许多层的子键。这个结构就象一个文件系统,根键就象一个根目录,子键就象子目录,你就象嵌套子目录一样嵌套子键。
你可以用Regedit来检查系统注册表中的信息,只要你不修改或者删除任何数值或者子键,你就不会存在任何损害操作系统或者你的软件/硬件的配置的危险。实际上,查看注册表可以学到很多东西。
SOFTWARE是HKEY_LOCAL_MACHINE键中一个子键,如果你检查你的计算机中的注册表信息,你会发现其它的软件商已经使用了HKEY_LOCAL_MACHINE\SOFTWARE子键。
本文使用的示例应用程序使用了四级注册表,虽然它可以使用更多的子键嵌套。我们将使用子键SOFTWARE\RegistryTest\Configuration\TestValue,TestValue是我们想存储配置变量的地方。在我们的例子中,我们希望在SOFTWARE下找到嵌套的RegistryTest,以及在RegistryTest下嵌套的Configuration,我们示例应用程序的名称叫RegistryTest,我们想在一个Configuration字段中存储数据。
使用VJ6访问注册表
我们可以通过VJ6中的J/Direct来访问Windows API。但是在实践中我们不需要调用API。VJ6本身包括了访问注册表的类,你可以通过这些类进行所有的注册表访问。除非你的要求特别复杂。
这些类位于com.ms.wfc.app库中。Registry类可以很方便地得到注册表的根键,在这里根键是一个RegistryKey对象。一个RegistryKey对象代表注册表中的一个键或者子键,你可以使用RegKey类或者RegistryKey类,RegKey类提供的选择较少,你只能访问四个根键:
HKEY_CLASSES_ROOT
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE以及HKEY_USERS。当使用RegKey类时,数据的类型被限制在字符串、整型和字节类型。我们在本文中将使用RegistryKey类以及相关的Registry类。
RegistryKey类被定义为final类型,因此你无法通过new这个操作符来声明一个实例,你也不能继承它。为了得到RegistryKey的一个实例,你可以使用静态的getBassClass,也可以使用 Registry类的成员函数。下面的代码对两种方法各举一例。我喜欢使用Registry类中的一个成员函数,因为当我想对注册表立即进行操作时可以节省一行代码。下面的例程首先获得RegistryKey类的一个实例,然后对注册表立即进行一个操作。在这里是打开一个子键,如果这个子键不存在的话,将创建一个子键。
//Creating an instance of RegistryKey class with the static getBaseKey() method; Performing
//an immediate action on a subkey takes an extra line of code.
RegistryKey objRegistryKey =
RegistryKey.getBaseKey(HKEY_CURRENT_USER);
ObjRegistry.createSubkey(
"Software\\RegistryTest\\Configuration");
//Creating an instance of the RegistryKey class with the CURRENT_USER member of the
//Registry class. We create an instance of RegistryKey and perform an action with just one
//line of code.
RegistryKey objRegistryKey = Registry.CURRENT_USER.
CreateSubKey)"Software\\RegistryTest\\Configuration");
RegistryKey类为创建、删除以及获取子键中的信息提供了强有力的功能,对于数据值也具有相同的功能。
访问注册表中的数据值的代码也是非常简单的。如果子键存在,getSubKey将返回一个Registry对象,否则将返回一个null。如果你只需要读取一个数据值并且需要确保安全,getSubKey这个方法有一个布尔值的选项可以让你设置成只读的模式。唯一的技巧就是对getValue返回的值进行类型转换。记住注册表是一个二进制文件,你可以直接存储多种数据类型。getValue将返回一个对象并需要一个显式的类型转换。在下面的代码中,数据值被转换成字符串的类型:
objRegistryKey = Registry.CURRENT_USER.getSubKey(
"Software\\RegistryTest\\Configuration", true);
if (objRegistryKey = = null)
alertNoSubKey("HKEY_CURRENT_USER\\Software" +
"\\RegistryTest\\Configuration");
else
{
strSetting = (String)objRegistryKey.getValue(
"TestValue", "");
objRegistryKey.close();
}
设置一个数据值的代码更加简单,不需要进行类型转换。记住把getSubKey中代表只读参数的布尔变量改成false。此外,你将会得到一个WFC例外。如果你不传递一个布尔值,则自动假设成false,可以对子键进行读/写访问。因此如果你愿意的话,你可以不指定布尔值这个参数:
objRegistryKey = Registry.CURRENT_USER.getSubKey(
"Software\\RegistryTest\\Configuration", false);
if (objRegistryKey = = null)
alertNoSubKey("HKEY_CURRENT_USER\\Software" +
"\\RegistryTest\\Configuration");
else
{
strSetting = (String)objRegistryKey.setValue(
"TestValue", "Some value");
objRegistryKey.close();
}
删除一个子键更加简单。一样,确认把getSubKey布尔值这个参数设置成false或者干脆不带这个参数:
objREgistryKey = Registry.CURRENT_USER.getSubKey(
"Software\\RegistryTest\Configuration", false);
if (objRegistryKey = = null)
alertNoSubKey ("HKEY_CURRENT_USER\\Software" +
"\\RegistryTest\\Configuration");
else
{
strSetting =
(String)objRegistryKey.DeleteValue ("TestValue");
objRegistryKey.close ();
}
创建一个测试程序
本文章中的范例工程文件使用WFC生成一个对话框,用来测试对四个不同根键的访问。程序在HKEY_CURRENT_USER根键下缺省创建一个叫做\SOFTWARE\RegistryTest\Configuration的子键。如果你愿意的话可以改变这个缺省设置。(在本文中讨论的范例程序可以从网上下载,详见本文最后)。
VJ6和MFC的一个好处就是你只需要编写很少几行的代码就能够创建一个对话框。创建对话框的过程和VB的form designer是非常相似的。你从工具箱中把组件拖出来然后把它丢到对话框中,你可以在代码以及/或者属性窗口中设置属性。
测试程序用一个集合框(group box)把收音机按钮框在一起,通过收音机按钮来选择四个根键中的一个。此外还有命令行按钮,标签和编辑框。
虽然程序的代码好象又长又复杂,绝大多数都是用VJ6的Forms Designer进行设计的。我增加了点击收音机按钮和命令行按钮所对应的事件。你可以用测试程序对注册表的数据元素进行插入、更新以及删除操作。
警告:注意使用这个程序的风险性!不要删除或者修改除了测试程序缺省设置之外的注册表项。
有三种操作注册表的私有方式:getSetting、saveSetting和deleteSetting。另外还有两种私有方式提供额外支持:composeSubKey,它能够把编辑框中的文字组合成为一个子键字符串;另一个是alertNoSubKey,当用户试图读取或者删除一个不存在的子键时,它将产生一个对话框。
因为测试程序初始化了一个和Registry对象相关的RegistryKey对象,它在获取、保存和删除设置时使用了一个switch语句,switch语句对选择特定根键所对应的RegistryKey对象进行初始化。
无限的可能性
当今的用户希望软件易于定制,容易进行控制。注册表的设置允许你的应用程序记住用户的个性设置。同时它还为应用程序的性能数据提供了一个存储的地点,因此你的应用程序可以根据用户的需求进行自我优化。
比如,你可以在注册表中保存最近使用过的文件。Word和电子数据表能够这样,你的应用程序也可以这样。你可以使用getValueCount来确定在你的最近使用列表(MRU)中存在多少文件。这个计数器可以控制一个for的循环。有些应用程序设计很巧妙,它们在MRU中使用文件名作为数据值,这就好象把它们放在变量赋值的左边,而它们在数据值中输入了一个空白字符串。为什么?因为它们可以用一个单一的命令使用getValueNames来返回他们MRU中的所有文件,而不需要用一个for循环来列举数据值。就象我所说的那样--巧妙。此外,我是通过查看我的一台计算机上的系统注册表的选项而学到这个技巧的。再次重复一遍,查看注册表经常可以得到很大的收获。
当为不同的用户存储应用程序数据时,记住把HKEY_CURRENT_USER作为键。当你存储那些对于一台计算机上的所有用户都必须相同的应用程序数据时,把HKEY_LOCAL_MACHINE作为键。
注册表也可以用于不太重要的场合。它也是一个存储窗口位置和窗口大小设置、URL等等数据的理想的位置。你可以在你的Java应用程序中使用系统注册表。Visual J++ 6.0方便了对注册表的访问,当你访问注册表时只要遵守几条规则就可以了。如果你胆敢于在注册表中冒险时,唯一的障碍就是你的独创性了。