1.概述
注册表是一个存储设备,包含有关应用程序、用户和默认系统设置的信息。例如,应用程序可以使用注册表来存储应用程序关闭后需要保留的信息,并可在应用程序启动时访问这些信息。例如,可以存储颜色首选项、屏幕位置或窗口大小。通过将信息存储在注册表中的不同位置,可以为各位用户分别控制这些数据。
Pocket PC应用程序开发完成并分发到用户手中后,在应用程序运行时同样可能需要记录一些程序配置信息,例如需要存储一个序列号或者一个版本信息,或者在Pocket PC应用程序的Logo界面里显示用户姓名和工作单位等信息。在.NET Compact Framework 2.0中,新增了Microsoft.Win32命名空间的Registry类。通过Registry类可以很容易地在Pocket PC应用程序里处理Pocket PC设备的注册表。
本章将介绍如何通过.NET Compact Framework进行Windows Mobile注册表的应用和开发,并提供一个读取Pocket PC设备Owner信息的注册表应用开发示例。
2.Windows Mobile注册表
在Windows Mobile操作系统中,系统配置信息仍然集中存储在称为注册表的分层数据库中。注册表代替了以往在.ini、.sys和.com等文件里存储应用程序配置信息的实现方法。
2.1注册表结构
注册表是按照子树(根项)、子树的项、子项和值项的层次结构组织的。由于每台Pocket PC设备上安装的硬件设备、系统服务和程序有所不同,因此一台Pocket PC设备上的注册表内容可能与另一台有很大的不同。
注册表项可以有子项,同样子项也可以有子项。尽管注册表中的大多数信息都存储在磁盘上,并且被永久保存,但是存储在某些子项中的一些信息,在每次操作系统启动时都会被覆盖。
2.2注册表子树
图18-2 注册表项中的值项
Windows Mobile具有两个注册表子树:HKEY_LOCAL_MACHINE和HKEY_USERS。不过,为了使注册表中的信息易于查找,在用Visual Studio 2005自带的“远程注册表编辑器”工具查看时显示有4棵子树,其中两棵是注册表其他部分的别称。表18-1列出并说明了这4棵子树。图18-1展示了Windows注册表和Windows Mobile注册表之间的差别。
表18-1 注册表子树
根项名称 |
说明 |
HKEY_LOCAL_MACHINE |
包含关于本地计算机系统的信息,包括硬件和操作系统数据,如总线类型、系统内存、设备驱动程序和启动控制数据 |
HKEY_CLASSES_ROOT |
包含用于各种OLE技术和文件类关联数据的信息。如果HKEY_CURRENT_USER/SOFTWARE/Classes或HKEY_LOCAL_MACHINE/SOFTWARE/Classes中存在相应的项或值,则在HKEY_CLASSES_ROOT中会存在某个特定的项或值。如果两处均存在项或值,则HKEY_CURRENT_USER版本将是出现在HKEY_CLASSES_ROOT中的那一个 |
HKEY_CURRENT_USER |
包含当前以交互方式(与远程方式相反)登录的用户的用户配置文件,包括环境变量、桌面设置、网络连接、打印机和程序首选项。该子树是 HKEY_USERS 子树的别名,它指向 HKEY_USERS/当前用户的安全 ID |
HKEY_USERS |
包含关于动态加载的用户配置文件和默认配置文件的信息,它包含同时出现在 HKEY_CURRENT_USER 中的信息。正在远程访问服务器的用户,在服务器上的该项下没有配置文件;他们的配置文件将加载到自己计算机的注册表中 |
图18-1 Windows和Windows Mobile注册表之间的差别
每个根项名均以“HKEY_”为前缀,以便向软件开发人员指出这是可以由程序使用的句柄。句柄是一个值,用于识别资源。有了句柄,程序就能对资源进行访问。
2.3注册表值项
每个注册表项或子项都可以包含称为值项的数据。有些值项存储特定于每个用户的信息,而其他值项则存储应用于计算机所有用户的信息。值项具有3部分:值的名称、值的数据类型和该值本身。通过Visual Studio 2005自带的“远程注册表编辑器”工具查看时,值项的显示如图18-2所示。
图18-2 注册表项中的值项
3.使用注册表
在.NET Compact Framework中,Microsoft.Win32命名空间只提供处理操作系统注册表的类和对象。表18-3列出了Microsoft.Win32命名空间支持的类和对象。
表18-3 Microsoft.Win32命名空间支持的类和对象
类名 |
说明 |
Registry |
提供表示Windows注册表中的根项的RegistryKey对象,并提供访问项/值对的static方法 |
RegistryKey |
表示Windows注册表中的项级节点。此类是注册表封装 |
RegistryKeyPermissionCheck |
指定在打开注册表项并访问它们的名称/值对时是否执行安全检查 |
RegistryValueKind |
指定在注册表中存储值时所用的数据类型,或标识注册表中某个值的数据类型 |
Registry类包含在Windows Mobile的注册表中能找到的标准子树集。由Registry类公开的RegistryKey实例,描绘注册表中的子树项和值的基本存储机制。所有的项都是只读的,因为注册表依赖于它们的存在。由Registry类以静态成员的方式公开的子树有:
· CurrentUser 存储有关用户首选项的信息。
· LocalMachine 存储本地计算机的配置信息。
· ClassesRoot 存储有关类型(和类)及其属性的信息。
· Users 存储有关默认用户配置的信息。
一旦标识了希望在其下存储/检索注册表信息的子树后,就可以开始使用RegistryKey类创建或删除子项和给指定项赋值。
3.1创建注册表子项
在.NET Compact Framework中,可以调用RegistryKey类的CreateSubKey方法来实现创建注册表子项的功能。调用CreateSubKey方法,创建一个新子项或打开一个现有子项以进行写访问。.NET Compact Framework只提供了CreateSubKey方法的一种重载版本。该方法的声明语法如下:
Public Function CreateSubKey (ByVal subkey As String) As RegistryKey
字符串subkey不区分大小写,指定要创建或打开的子项的名称或路径。CreateSubKey方法返回一个标识新建子项的RegistryKey对象。如果操作失败,则返回空引用(在Visual Basic中为Nothing)。如果为subkey指定了零长度字符串,则返回当前的RegistryKey对象。以下代码演示调用RegistryKey类的CreateSubKey方法。
……
' 在 HKEY_CURRENT_USER 下创建一个名为 RegistryDemo 的子项
Dim RegistryDemo As Microsoft.Win32.RegistryKey = _
Microsoft.Win32.Registry.CurrentUser.CreateSubKey("RegistryDemo")
' 在 HKEY_CURRENT_USER/RegistryDemo 下创建两个子项
RegistryDemo.CreateSubKey("TestName").Close()
Dim TestSettings As Microsoft.Win32.RegistryKey = RegistryDemo.CreateSubKey("TestSettings")
' 关闭 RegistryKey 实例
TestSettings.Close()
RegistryDemo.Close()
……
3.2 读取注册表子项
读取注册表子项的功能指检索根项或指定子项的子项。RegistryKey类的GetSubKey Names方法能够非常方便地检索根项或指定子项的子项。GetSubKeyNames方法的声明语法如下:
Public Function GetSubKeyNames As String()
GetSubKeyNames方法返回一个包含当前项的子项名称的字符串数组。以下代码演示调用RegistryKey类的GetSubKeyNames方法,检索HKEY_CURRENT_USER的所有子项。
……
Dim subKeyName As String
Dim subkeys As Microsoft.Win32.RegistryKey = Microsoft.Win32.Registry.CurrentUser
' 返回 HKEY_CURRENT_USER 的所有子项
Dim keynames As String() = subkeys.GetSubKeyNames()
' 显示子项名称
For Each subKeyName In keynames
MsgBox(subKeyName)
Next
subkeys.Close()
……
3.3 创建注册表值项
创建注册表值项的功能是指设置注册表项中的名称/值对的值。通过RegistryKey类的SetValue方法和Registry类的SetValue方法,都可以实现创建注册表值项的功能。但是每次使用Registry类的SetValue方法时,SetValue方法都会尝试打开和关闭注册表项,所以当访问大数据量的值项时,建议使用RegistryKey类的SetValue方法来保证更好的性能。在.NET Compact Framework中,RegistryKey类的SetValue方法提供两个重载版本,其声明语法如下:
Public Shared Sub SetValue ( _
ByVal keyName As String, _
ByVal valueName As String, _
ByVal value As Object, _
)
Public Shared Sub SetValue ( _
ByVal keyName As String, _
ByVal valueName As String, _
ByVal value As Object, _
ByVal valueKind As RegistryValueKind _
)
SetValue方法的通过RegistryValueKind参数指定的注册表枚举数据类型,设置该指定的注册表项的名称/值对。如果指定的值项不存在,则创建该值项。注册表值项,可具有与任何名称都不关联的值。如果注册表编辑器中显示此未命名的值,则会出现字符串“(Default)”,而不会出现值项的名称。若要设置这个未命名的值,则指定valueName为空引用或空字符串即可。如果指定的value类型与指定的valueKind不匹配,并且无法转换数据,则会抛出ArgumentException例外。
以下代码演示调用RegistryKey类的SetValue方法设置TestSettings子项的3个名称/值对:
……
' 在 HKEY_CURRENT_USER 下创建一个名为 RegistryDemo 的子项
Dim RegistryDemo As Microsoft.Win32.RegistryKey = _
Microsoft.Win32.Registry.CurrentUser.CreateSubKey("RegistryDemo")
' 在 HKEY_CURRENT_USER/RegistryDemo 下创建两个子项
RegistryDemo.CreateSubKey("TestName").Close()
Dim TestSettings As Microsoft.Win32.RegistryKey = RegistryDemo.CreateSubKey("TestSettings")
' 设置 TestSettings 子项的3个名称/值对
Dim myStrings() As String = {"One", "Two", "Three"}
TestSettings.SetValue("TestArray", myStrings)
TestSettings.SetValue("语言", "英语")
TestSettings.SetValue("级别", "CET-6")
TestSettings.SetValue("ID", 1001)
' 关闭 RegistryKey 实例
TestSettings.Close()
RegistryDemo.Close()
……
3.4 读取注册表值项
读取注册表值项的功能,包括检索与指定子项关联的所有值名称和值项所对应的值两部分功能。通过RegistryKey类的GetValueNames方法,可以实现检索与指定子项关联的所有值名称的功能。通过RegistryKey类的GetValue方法和Registry类的GetValue方法,都可以实现检索子项所对应项值的功能。但是每次使用Registry类的GetValue方法时,GetValue方法都会尝试打开和关闭注册表项,所以当访问大数据量的值项时,建议使用RegistryKey类的GetValue方法。RegistryKey类的GetValueNames方法的声明语法如下:
Public Function GetValueNames As String()
在.NET Compact Framework中,RegistryKey类的GetValue方法提供两个重载版本,其声明语法如下:
Public Function GetValue (ByVal name As String) As Object
Public Function GetValue (ByVal name As String, ByVal defaultValue As Object) As Object
GetValueNames方法返回一个包含与此项关联的所有值名称的字符串数组。如果未找到此项的值名称,则返回一个空数组。注册表子项可以有一个默认值,该默认值的名称为空字符串("")的名称/值对。如果为注册表子项设置了默认值,则GetValueNames方法返回的数组包含该空字符串。
GetValue方法返回一个与指定注册表项中的指定名称关联的值。如果在指定的项中未找到该名称,则返回用户提供的默认值;或者如果指定的项不存在,则返回空引用。以下代码演示调用RegistryKey类的GetValueNames方法,检索TestSettings子项的所有值项的名称,并通过GetValue方法显示值项对应的值。
……
Dim valueName As String
Dim subkeys As Microsoft.Win32.RegistryKey = _
Microsoft.Win32.Registry.CurrentUser.OpenSubKey("RegistryDemo/TestSettings")
' 返回 TestSettings 的所有值项
Dim keyvalues As String() = subkeys.GetValueNames()
' 显示值项名称和对应的值
For Each valueName In keyvalues
MsgBox("名称/值对 " & valueName & "=" & subkeys.GetValue(valueName).ToString())
Next
subkeys.Close()
……
3.5 删除注册表子项
在.NET Compact Framework中,提供了两种删除注册表子项的方法。分别是RegistryKey类的DeleteSubKeyTree和DeleteSubKey方法。DeleteSubKeyTree方法递归删除指定子项以及该子项所包括的任何子项。DeleteSubKey方法删除指定的子项。在.NET Compact Framework中,DeleteSubKey方法提供两个重载版本。DeleteSubKeyTree和DeleteSubKey方法的声明语法如下:
Public Sub DeleteSubKeyTree (ByVal subkey As String)
Public Sub DeleteSubKey (ByVal subkey As String)
Public Sub DeleteSubKey (ByVal subkey As String, ByVal throwOnMissingSubKey As Boolean)
RegistryKey类的这两个方法的字符串参数subkey均不区分大小写。使用DeleteSubKeyTree方法删除指定子项时,将删除目录树中该项下的所有项,并且不提供任何警告提示。如果仅希望删除不包括其他子项的子项时,可以使用DeleteSubKey方法。DeleteSubKey方法的throwOnMissingSubKey参数,指示在找不到指定子项的情况下是否引发异常。如果该参数为true,并且指定的子项不存在,则引发异常;如果该参数为false,并且指定的子项不存在,则不执行任何操作。以下代码演示调用RegistryKey类的DeleteSubKey和DeleteSubKeyTree方法,分别删除子项TestSettings和RegistryDemo。
' 删除子项 TestSettings 和 RegistryDemo
Microsoft.Win32.Registry.CurrentUser.DeleteSubKey("RegistryDemo/TestName")
Microsoft.Win32.Registry.CurrentUser.DeleteSubKeyTree("RegistryDemo")
……
18.3.6 删除注册表值项
删除注册表值项的功能是指从当前子项中删除指定的值项。通过RegistryKey类的DeleteValue方法,可以实现删除注册表值项的功能。在.NET Compact Framework中,RegistryKey类的DeleteValue方法提供两个重载版本,其声明语法如下:
Public Sub DeleteValue (ByVal name As String)
Public Sub DeleteValue (ByVal name As String, ByVal throwOnMissingValue As Boolean)
DeleteValue方法通过RegistryValueKind参数指定的注册表枚举数据类型,设置该指定的注册表项的名称/值对。DeleteValue方法的throwOnMissingSubKey参数,指示在找不到指定值的情况下是否引发异常。如果该参数为True,并且指定的值项不存在,则引发异常;如果该参数为False,并且指定的值项不存在,则不执行任何操作。以下代码演示调用RegistryKey类的DeleteValue方法删除值项TestArray。
……
Dim testSettings As Microsoft.Win32.RegistryKey = _
Microsoft.Win32.Registry.CurrentUser.OpenSubKey("RegistryDemo/TestSettings")
' 删除值项 TestArray
testSettings.DeleteValue("TestArray")
testSettings.Close()
……
18.3.7 注册表数据类型
表18-2列出了.NET Compact Framework 2.0支持的7种常用的Windows Mobile注册表数据类型。这7种注册表数据类型的定义,包含在Microsoft.Win32命名空间的枚举对象RegistryValueKind里。
设置注册表项的值时,应该使用SetValue方法显式地指定注册表数据类型。检索和显示注册表项的值时,应该先使用GetValueKind方法检查注册表数据类型。
表18-2 Windows Mobile注册表数据类型
数据类型 |
说明 |
String |
固定长度的文本字符串。此值与 Win32 API 注册表数据类型 REG_SZ 等效 |
Binary |
指定任意格式的二进制数据。此值与 Win32 API 注册表数据类型 REG_BINARY 等效 |
DWORD |
数据由4字节长的数表示。设备驱动程序和服务的很多参数都是这种类型,这些参数在注册表编辑器中是以二进制、十六进制或十进制的格式显示的。此值与 Win32 API 注册表数据类型 REG_DWORD 等效 |
MultiString |
多重字符串。包含列表或多值(其格式可被用户读取)的值通常为该类型。各个值项之间可以使用空格、逗号或其他标记分开。此值与 Win32 API 注册表数据类型 REG_MULTI_SZ 等效 |
ExpandString |
指定一个以 NULL 结尾的字符串,该字符串中包含对环境变量(如 %PATH%,当值被检索时,就会展开)的未展开的引用。此值与 Win32 API 注册表数据类型 REG_EXPAND_SZ 等效 |
QWord |
指定一个 64 位二进制数。此值与 Win32 API 注册表数据类型 REG_QWORD 等效 |
Unknown |
指示一个不受支持的注册表数据类型。例如,不支持 Microsoft Win32 API 注册表数据类型 REG_RESOURCE_LIST。使用此值指定 SetValue 方法,应在存储名称/值对时确定适当的注册表数据类型 |
清单18-1演示调用RegistryKey类的SetValue、GetValueKind方法和枚举对象RegistryValueKind设置及检查显示各种类型的注册表数据。
清单18-1 设置及检查显示各种类型的注册表数据
……
Dim rk As Microsoft.Win32.RegistryKey = _
Microsoft.Win32.Registry.CurrentUser.CreateSubKey("RegistryDemo/RegistryValueKindExample")
' 创建名称/值对
rk.SetValue("QuadWordValue", 42, Microsoft.Win32.RegistryValueKind.QWord)
rk.SetValue("DWordValue", 42, Microsoft.Win32.RegistryValueKind.DWord)
rk.SetValue("MultipleStringValue", New String() {"One", "Two", "Three"}, _
Microsoft.Win32.RegistryValueKind.MultiString)
rk.SetValue("BinaryValue", New Byte() {10, 43, 44, 45, 14, 255}, _
Microsoft.Win32.RegistryValueKind.Binary)
rk.SetValue("StringValue", "The path is %PATH%", Microsoft.Win32.RegistryValueKind.String)
rk.SetValue("ExpandedStringValue", "The path is %PATH%", _
Microsoft.Win32.RegistryValueKind.ExpandString)
' 显示名称/值对
Dim valueNames As String() = rk.GetValueNames()
Dim s As String
For Each s In valueNames
Dim rvk As Microsoft.Win32.RegistryValueKind = rk.GetValueKind(s)
Select Case rvk
Case Microsoft.Win32.RegistryValueKind.MultiString
Dim values As String() = CType(rk.GetValue(s), String())
MsgBox(s & ":" & rvk & ":" & values(0))
Dim i As Integer
For i = 1 To values.Length - 1
MsgBox(i & ":" & values(i))
Next
Case Microsoft.Win32.RegistryValueKind.Binary
Dim bytes As Byte() = CType(rk.GetValue(s), Byte())
MsgBox(s & ":" & rvk & ":" & bytes(0))
Dim i As Integer
For i = 1 To bytes.Length - 1
MsgBox(s & ":" & bytes(i))
Next
Case Else
MsgBox(s & ":" & rvk & ":" & rk.GetValue(s))
End Select
Next
……
执行清单18-1的代码,向Pocket PC设备注册表写入数据的结果如图18-3所示。
图18-3 各种类型的注册表数据
通过本章的介绍,应该对如何使用Windows Mobile注册表有了一定的了解。接下来通过介绍一个Pocket PC设备的注册表应用实例来加深感性认识。
在Windows Mobile中,系统配置信息集中位于注册表中。这样做虽然简化了对Pocket PC设备的管理,但对注册表的一个错误编辑却有可能使操作系统瘫痪,而不得不进行Pocket PC设备所特有的原始系统恢复操作。
由于Windows Mobile属于单用户操作系统,因此不存在对Pocket PC设备用户进行访问权限的设置。
在.NET Compact Framework应用程序中删除子项之前,必须先显式关闭子项的所有打开的实例及该子项自己的子级子项。Windows Mobile规定的最大子项深度为15级。
当Pocket PC应用程序在一组子项中保存或检索大量注册表设置时,将执行许多冗余安全检查。应该通过RegistryKeyPermissionCheck枚举对象指定何时忽略对子项执行的安全检查。
6 小结
在.NET Compact Framework 2.0中,通过新增的Microsoft.Win32命名空间的Registry类,可以很容易地编程实现在Pocket PC应用程序里处理Pocket PC设备的注册表。通过对注册表的操作,可以将Pocket PC应用程序的设置信息,保存到注册表中或将其序列化到配置文件中,在运行时保留应用程序设置。在使用注册表记录数据时,不但应该注意操作安全性,还应当限制存储在注册表中的数据量。无论如何,注册表都为开发移动应用解决方案提供了另一种处理数据的选择。