⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 10.txt

📁 VB文章集(含API、窗口、数据库、多媒体、系统、文件、等等)
💻 TXT
📖 第 1 页 / 共 3 页
字号:
VB 与Windows API 讲座(五)

王国荣

病从口入, 这句话大致上也适用於电脑, 如果我们不??给电脑不明的软体或档案,就不用担心电脑病毒的肆虐, 不过电脑之所以出状况, 电脑病毒只是其中一种原因,虽然它是最容易联想到的原因, 但实际上, 就像人一样, 除了部分的病痛来自病毒的感染之外,仍有不少的病痛来自於身体组织的毛病。

 

对 Windows 来说, 哪些是最重要的器官组织呢?除了驱动程式与应用程式之外,当首推登录资料库(Registry Database), 举例来说, 开机时, Windows 会从登录资料库中读取硬体的相关设定,如果登录资料库的设定与硬体不符合, 就可能无法开机或者 Windows 必须重新进行硬体的侦测,除了硬体的设定之外, 软体的运作也与登录资料库有极大的关系, 举例来说,当我们安装 Windows 之後, .bmp 档案预设的开启程式是「小画家」, 但是当我们又安装了其他绘图软体, .bmp 的开启程式却可能变成新安装的绘图软体, 这是因为新安装的绘图软体改变了登录资料库所致。

 

显然地, 能够了解 Windows 的登录资料库, 在 Windows 出状况时, 将更容易解决问题,而对程式设计者而言, 甚至可以藉着写入资料到登录资料库而达到改变 Windows 或应用程式行为的目的。

 


--------------------------------------------------------------------------------
登录资料库的组织架构 
--------------------------------------------------------------------------------

 

存取登录资料库以前, 必须先了解登录资料库的组织架构, 而了解登录资料库的组织架构最简单的方法便是启动 Windows 提供的「登录编辑程式」, 启动的方法是利用「开始」工作列的「执行」交谈窗执行 RegEdit 程式, 执行之後, 可看到如图-1 的画面。

 

图-1 登录编辑程式

 

Key 与 Subkey 
--------------------------------------------------------------------------------

 

登录编辑程式的视窗结构与档案总管很像, 左边窗格的每一个资料夹图示表示一个 Key(中文的登录编辑程式将它翻译成「机码」, 但为了与程式直接对映, 本文中笔者不做翻译,仍以 Key 称之), 而就像资料夹底下还有子资料夹一样, 登录资料库的 Key 底下也有 Subkey(翻译成「子机码」, 但本文也一样不翻译), 为了完整地表示某一个 Subkey,习惯上是采用资料夹的路径表示法, 举例来说, HKEY_LOCAL_MACHINE 之下的 "Software" Subkey 表示成 HKEY_LOCAL_MACHINE\Software, 而 "Software" 之下的 "Microsoft" Subkey 则表示成 HKEY_LOCAL_MACHINE\Software\Microsoft。

 

图-2 Key 与 Subkey

 

Value、Value Name、Value Data 与 Default Value 
--------------------------------------------------------------------------------

 

当我们在登录编辑程式左边窗格选取某一个 Key(或 Subkey) 之後, 出现在右边窗格的是这个 Key 的 Value(数值), Value 可分成 Name(名称) 及 Data(资料) 两部分, 对每一个 Key 而言, 至少都含有一个 Default Value(预设值) 的栏位, 以 "HKEY_CLASSES_ROOT\.txt" Subkey 为例, 其 Default Value 的内容等於 "txtfile", 而除了 Default Value 之外, 这个 Subkey 还含有 Name(名称)为 "Content Type" 而 Data(资料)为 "text/plain" 的 Value, 参阅图-3。

 

图-3 Value、Value Name、Value Data 与 Default Value

 


--------------------------------------------------------------------------------
存取 Value, 先取 Key Handle 
--------------------------------------------------------------------------------

 

了解登录资料库的组织架构之後, 接下来该如何存取呢?就像我们存取档案时,必须指明档案的所在资料夹(目录)一样, 存取登录资料库时, 则必须先指明 Key。Key 在登录编辑程式中所看到的是一长串的字串, 例如 "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion",但是在 Windows 内部, 每一个 Key 都会对应到一个 Key Handle(等於一个长整数值,程式中通常以 hKey 表示), Windows 之所以要以 hKey 来代表 Key 是为了让登录资料库的存取更有效率,因为整数的操作效能要优於字串, 所以我们的第一个课题便是如何取得 Key 的 Key Handle(hKey)。

 

首先是位於最上层的 Key, 包含 HKEY_CLASSES_ROOT、HKEY_CURRENT_USER、HKEY_LOCAL_MACHINE…等,这些 Key 的 hKey 是固定不变的, 其值如下表:

 

Key Key Handle 
HKEY_CLASSES_ROOT &H80000000 
HKEY_CURRENT_CONFIG &H80000005 
HKEY_CURRENT_USER &H80000001 
HKEY_DYN_DATA &H80000006 
HKEY_LOCAL_MACHINE &H80000002 
HKEY_USERS &H80000003 

 

但如果要取得这些 Key 的 Subkey Handle, 则必须呼叫 RegOpenKey API 函数, RegOpenKey 含有叁个参数, 意义如下:

 

(1) ByVal hKey As Long:Key Handle。

(2) ByVal lpSubkey As String:Subkey 的字串。

(3) phkResult As Long:若 RegOpenKey 成功, 则此一参数将传回 Subkey 的 hKey。

 

举例来说, 我们想取得 HKEY_LOCAL_MACHINE 之下的 "SOFTWARE\Microsoft" Subkey, 则使用的叙述是:

 

Dim ret As Long, hKey As Long

ret = RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\Microsoft", hKey)

If ret = 0 Then ' 表示成功,

' hKey 的值即等於 "SOFTWARE\Microsoft" Subkey 的 Key Handle

End If

 

请注意呼叫登录资料库 API 函数(例如以上的 RegOpenKey)之後, 若成功,将传回 0, 否则传回非 0 的值, 这一点与 VB 函数的惯例并不相同, 请注意。

 

RegOpenKey 的第一个参数 hKey 除了可以指定最上层的 Key Handle 值(例如 HKEY_CLASSES_ROOT、HKEY_LOCAL_MACHINE…等)之外, 也可以是一个 Subkey Handle,以上一段程式为例, hKey 等於 "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft" 的 Subkey Handle, 接着如果我们要取得 "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion" 的 Subkey Handle, 则程式如下:

 

Dim ret As Long, hKey As Long, hKey2 As Long

ret = RegOpenKey(hKey, "Windows\CurrentVersion", hKey2)

' 则 hKey2 将等於 "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft" 的

' "Windows\CurrentVersion" Subkey Handle

 

在以上程式中, 请注意不要在 "Windows\CurrentVersion" 之前加上 "\" 使成为 "\Windows\CurrentVersion", 这是错误的表示法。

 


--------------------------------------------------------------------------------
Value 的存取 
--------------------------------------------------------------------------------

 

在登录资料库的存取中, 主要分成 Key 及 Value 两大部分, 首先让我们来了解 Value 的存取。在 Value 的存取方面, Windows API 提供的函数有 RegQueryValue、RegQueryValueEx、RegEnumValue、RegEnumValueEx …等, 这几个函数的宣告式请参本文附录, 从 VB 的习惯来看, 这几个函数并不好用,主要是因为它们使用了 As Any 的宣告方式, 使得一般使用者很容易传错参数,而无法得到正确的结果, 为了简化 Value 的存取, 笔者特别撰写一套符合 VB 习惯的 Value 存取函数, 这套函数已经将 API 函数封装起来, 也就是说, 您只要使用笔者所提供的 Value 存取函数, 可以暂时不必了解比较艰涩的 API 函数, 当然, 如果您对 Value 存取的 API 函数有兴趣, 可先参阅附录的原始程式码, 至於详细的介绍,由於篇幅有限, 笔者将来会以专书说明, 本文则暂且略过。

 

笔者所提供的 Value 存取函数 
--------------------------------------------------------------------------------

 

首先参阅本文附录或者请进入笔者的网站下载相关的原始程式码, 笔者所提供的 Value 存取函数有:GetDefaultValue(读取 Default Value)、GetValue(读取特定 Value)、GetValueByIndex(读取任意 Value)、SetDefaultValue(写入 Default Value)、SetValue(写入特定 Value) 等 5 个, 兹说明如下:

 

◆ GetDefaultValue 函数:读取Default Value

 

此一函数的定义是:

 

Function GetDefaultValue(ByVal hKey As Long, ByVal Subkey As String, Value As String) As Boolean

 

其中 hKey 及 Subkey 参数用来传入欲读取的 Key 或 Subkey, 而 Value 则用来传回读取之资料,若呼叫成功, 此一函数将传回 True。假设我们想读取 "HKEY_CLASSES_ROOT\.txt" 的 Default Value, 则呼叫的范例如下:

 

Dim S As String, ret As Boolean 

ret = GetDefaultValue(HKEY_CLASSES_ROOT, ".txt", S)

' 如果 ret 为 True, 则 S 等於读取之资料

' 如果 "HKEY_CLASSES_ROOT\.txt" 没有预设值, 则 S = ""

 

若已事先求得 "HKEY_CLASSES_ROOT\.txt" 的 Subkey Handle 值,则以下的呼叫也是正确的:

 

Dim S As String, ret As Boolean, hKey As Long

' 先呼叫 RegOpenKey 求取 "HKEY_CLASSES_ROOT\.txt" 的 Subkey Handle

ret = RegOpenKey(HKEY_CLASSES_ROOT, ".txt", hKey)

' hKey 已是正确的 Subkey Handle,所以参数二 Subkey 只要传入空字串即可

ret = GetDefaultValue(hKey, "", S)

 

◆ GetValue 函数:读取特定Value

 

GetDefaultValue 只能读取某一个 Key(或 Subkey) 的 Default Value, 而 GetValue 则可以读取特定名称的 Value, 举例来说, 欲读取 "HKEY_CLASSES_ROOT\.txt" Subkey 之下名称为 "Content Type" 的 Value, 则必须使用 GetValue 函数。

 

GetValue 的函数定义是:

 

Function GetValue(ByVal hKey As Long, ByVal ValueName As String, Value() As Byte, vType As ValueType) As Boolean

 

请注意此一函数没有 Subkey 参数, 所以呼叫之前必须先取得 Subkey Handle,举例来说, 欲读取 "HKEY_CLASSES_ROOT\.txt" Subkey 之下名称为 "Content Type" 的 Value, 呼叫的过程如下:

 

Dim bArr() As Byte, vType As Long, hKey As Long

' 先呼叫 RegOpenKey 取得 "HKEY_CLASSES_ROOT\.txt" 的 Subkey Handle

ret = RegOpenKey(HKEY_CLASSES_ROOT, ".txt", hKey)

ret = GetValue(hKey, "Content Type", bArr, vType)

 

除了先取得 Subkey Handle 之外, GetValue 与 GetDefaultValue 另一个最大的不同在於:GetDefaultValue 所读取的资料一定是字串型别, 而 GetValue 所读取的资料除了字串之外, 还可能是其他类型的资料,在实务上, 笔者利用 bArr 及 vType 两种参数来接受 GetValue 所读回来的不同类型资料,呼叫之前, bArr 为一个空的 Byte 阵列(利用 Dim bArr() As Byte 产生), 若呼叫成功, bArr 将成为一个含有读取资料的 Byte 阵列(不再是空的 Byte 阵列), 而 vType 则等於所读取资料的类型, 它可能有以下几种类型:

 

资料类型 设定值
意义 
REG_SZ 1
字串 
REG_EXPAND_SZ 2
可展开式字串 
REG_MULTI_SZ 7
多重字串 
REG_BINARY 3
Binary 资料 
REG_DWORD 4
长整数 
REG_DWORD_BIG_ENDIAN 5
Big Endian 长整数 

 

您可能觉得奇怪, vType 含有以上几种类型, 为什麽读取的资料一律放在 Byte 阵列中(bArr 参数), 这是因为 Windows API 的设计使然, 为了将 Byte 阵列转换成不同型别的资料,笔者又撰写了以下的几个函数:

 

资料类型 意义 
ByteArrayToString 将 Byte 阵列转换成字串 
ByteArrayToMultiString 将 Byte 阵列转换成多重字串 
ByteArrayToLong 将 Byte 阵列转换成长整数 

 

使用以上的转换函数以前, 让笔者先说明 vType 几种资料类型的意义:

 

(1) REG_SZ:一般的字串, 若 vType 等於此一类型, 则呼叫 ByteArrayToString 进行转换。

(2) REG_EXPAND_SZ:也是呼叫 ByteArrayToString 进行转换, 但此类字串中含有 %WinDir% 之类的字串(注:%WinDir% 表示 Windows 的所在目录), 遇到此类字串,应该再呼叫 ExpandEnvironmentStrings API 函数将字串展开, 举例来说, 假设 Windows 的所在目录是 "C:\Windows", 则 "LoadHigh %WINDIR%\Command\DOSKey" 经过 ExpandEnvironmentStrings API 的转换之後, 将成为 "LoadHigh C:Windows\Command\DOSKey"。(特别说明:有些程式虽然写入字串资料到登录资料库时,所设定的资料类型为 REG_SZ, 但字串中仍然含有 %WinDir% 之类的字串, 因此若不能确定读取的字串中是否含有 %WinDir% 之类的字串, 则一律呼叫 ExpandEnvironmentStrings 是比较保险的)

(3) REG_MULTI_SZ:多重字串, 其结构如图-4, 此类资料, 若呼叫 ByteArrayToString 进行转换, 则得到如图-4 的字串, 为了转换此类字串, 笔者提供的副程式是 ByteArrayToMultiString, 呼叫前只要宣告一个空的字串阵列, 例如 Dim S() As String, 则转换之後, S 就等於含有多个字串的阵列了。

 

图-4 多重字串(REG_MULTI_SZ) 的意义

 

(4) REG_DWORD:表示含有 4 个字元的数值, 可呼叫 ByteArrayToLong 进行转换。

(5) REG_DWORD_BIG_ENDIAN:不同 CPU 对於数值的安排方式并不相同,所谓 Big Endian 指的是高位元组放在前面的安排方式(注:Intel 的 CPU 是低位元组放在前面,另外请注意 Windows NT 可能在非 Intel 的 CPU 底下执行), 虽然此一类型的位元组安排顺序有点不同,但是对程式而言, 欲取得正确的长整数, 也是呼叫 ByteArrayToLong 进行转换。

(6) REG_BINARY:Binary 资料, 使用 Byte Array 表示最为恰当,所以不必转换。

 

假设我们利用 GetValue 读取 Value 之後, 想将它显示出来, 则以下是参考范例:

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -