VB 版 (精华区)

发信人: bloom (├┝┞┟┠┢┣), 信区: VB
标  题: VB API教程(王国荣版)(五)(转载)
发信站: 哈工大紫丁香 (2000年09月07日18:42:50 星期四), 转信

【 以下文字转载自 cnTemp 讨论区 】
【 原文由 bloom 所发表 】
发信人: Love1976 (狄飞惊), 信区: VisualBasic       
发信站: BBS 水木清华站 (Thu Apr  6 04:21:43 2000)

发信人: coolknight (酷骑士~找工作中), 信区: VB
标  题: VB 與Windows API 講座(五)
发信站: 武汉白云黄鹤站 (Tue Nov  9 20:10:40 1999), 站内信件

VB 與Windows API 講座(五)
Windows 的登錄資料庫
------------------------------------------------------------------------
----
----
王國榮
 
病從口入, 這句話大致上也適用於電腦, 如果我們不餵給電腦不明的軟體或檔案
,就
不用擔心電腦病毒的肆虐, 不過電腦之所以出狀況, 電腦病毒只是其中一種原因
,雖
然它是最容易聯想到的原因, 但實際上, 就像人一樣, 除了部分的病痛來自病
毒的感
染之外,仍有不少的病痛來自於身體組織的毛病。
 
對 Windows 來說, 哪些是最重要的器官組織呢?除了驅動程式與應用程式之外,
當首
推登錄資料庫(Registry Database), 舉例來說, 開機時, Windows 會從登錄資
料庫
中讀取硬體的相關設定,如果登錄資料庫的設定與硬體不符合, 就可能無法開機
或者
Windows 必須重新進行硬體的偵測,除了硬體的設定之外, 軟體的運作也與登錄
資料庫
有極大的關係, 舉例來說,當我們安裝 Windows 之後, .bmp 檔案預設的開啟程
式是
「小畫家」, 但是當我們又安裝了其他繪圖軟體, .bmp 的開啟程式卻可能變成
新安裝
的繪圖軟體, 這是因為新安裝的繪圖軟體改變了登錄資料庫所致。
 
 
顯然地, 能夠瞭解 Windows 的登錄資料庫, 在 Windows 出狀況時, 將更容易
解決問
題,而對程式設計者而言, 甚至可以藉著寫入資料到登錄資料庫而達到改變 
Windows
或應用程式行為的目的。
 
------------------------------------------------------------------------
----
----
登錄資料庫的組織架構
------------------------------------------------------------------------
----
----
 
存取登錄資料庫以前, 必須先瞭解登錄資料庫的組織架構, 而瞭解登錄資料庫的
組織
架構最簡單的方法便是啟動 Windows 提供的「登錄編輯程式」, 啟動的方法是利
用「
開始」工作列的「執行」交談窗執行 RegEdit 程式, 執行之後, 可看到如圖
-1 的畫
面。
 
圖-1 登錄編輯程式
 
Key 與 Subkey
------------------------------------------------------------------------
----
----
 
登錄編輯程式的視窗結構與檔案總管很像, 左邊窗格的每一個資料夾圖示表示一
個 Ke
登錄編輯程式的視窗結構與檔案總管很像, 左邊窗格的每一個資料夾圖示表示一
個 Ke
y(中文的登錄編輯程式將它翻譯成「機碼」, 但為了與程式直接對映, 本文中筆
者不
做翻譯,仍以 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_CLA
SSES_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\Micro
soft\Windows\CurrentVersion",但是在 Windows 內部, 每一個 Key 都會對應
到一個
 Key Handle(等於一個長整數值,程式中通常以 hKey 表示), Windows 之所以要
以 h
Key 來代表 Key 是為了讓登錄資料庫的存取更有效率,因為整數的操作效能要優
於字串
, 所以我們的第一個課題便是如何取得 Key 的 Key Handle(hKey)。
 
首先是位於最上層的 Key, 包含 HKEY_CLASSES_ROOT、HKEY_CURRENT_USER、
HKEY_LOC
AL_MACHINE…等,這些 Key 的 hKey 是固定不變的, 其值如下表:
 
Key Key Handle
HKEY_CLASSES_ROOT &H80000000
HKEY_CURRENT_CONFIG &H80000005
HKEY_CURRENT_USER &H80000001
HKEY_DYN_DATA &H80000006
HKEY_DYN_DATA &H80000006
HKEY_LOCAL_MACHINE &H80000002
HKEY_USERS &H80000003
 
但如果要取得這些 Key 的 Subkey Handle, 則必須呼叫 RegOpenKey API 函數,
 Reg
OpenKey 含有三個參數, 意義如下:
 
(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 函數的慣例並不相同, 請注意。
 否則傳回非 0 的值, 這一點與 VB 函數的慣例並不相同, 請注意。
 
RegOpenKey 的第一個參數 hKey 除了可以指定最上層的 Key Handle 值(例如 
HKEY_CL
ASSES_ROOT、HKEY_LOCAL_MACHINE…等)之外, 也可以是一個 Subkey Handle,以
上一
段程式為例, hKey 等於 "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft" 的 Subkey
 Han
dle, 接著如果我們要取得 
"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Curr
entVersion" 的 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" 之前加上 "\" 使成為
 "\W
indows\CurrentVersion", 這是錯誤的表示法。
 
------------------------------------------------------------------------
----
----
Value 的存取
------------------------------------------------------------------------
----
----
 
在登錄資料庫的存取中, 主要分成 Key 及 Value 兩大部分, 首先讓我們來瞭解
 Val
在登錄資料庫的存取中, 主要分成 Key 及 Value 兩大部分, 首先讓我們來瞭解
 Val
ue 的存取。在 Value 的存取方面, Windows API 提供的函數有 RegQueryValue
、Reg
QueryValueEx、RegEnumValue、RegEnumValueEx …等, 這幾個函數的宣告式請參
本文
附錄, 從 VB 的習慣來看, 這幾個函數並不好用,主要是因為它們使用了 As 
Any 的
宣告方式, 使得一般使用者很容易傳錯參數,而無法得到正確的結果, 為了簡化
 Val
ue 的存取, 筆者特別撰寫一套符合 VB 習慣的 Value 存取函數, 這套函數已經
將 A
PI 函數封裝起來, 也就是說, 您只要使用筆者所提供的 Value 存取函數, 可
以暫時
不必瞭解比較艱澀的 API 函數, 當然, 如果您對 Value 存取的 API 函數有興
趣,
可先參閱附錄的原始程式碼, 至於詳細的介紹,由於篇幅有限, 筆者將來會以專
書說
明, 本文則暫且略過。
 
筆者所提供的 Value 存取函數
------------------------------------------------------------------------
----
----
 
首先參閱本文附錄或者請進入筆者的網站下載相關的原始程式碼, 筆者所提供的
 Valu
e 存取函數有:GetDefaultValue(讀取 Default Value)、GetValue(讀取特定 
Value)、
GetValueByIndex(讀取任意 Value)、SetDefaultValue(寫入 Default Value)、
SetVal
ue(寫入特定 Value) 等 5 個, 茲說明如下:
 
◆ GetDefaultValue 函數:讀取Default Value
 
此一函數的定義是:
此一函數的定義是:
 
Function GetDefaultValue(ByVal hKey As Long, ByVal Subkey As String, 
Value A
s String) As Boolean
 
其中 hKey 及 Subkey 參數用來傳入欲讀取的 Key 或 Subkey, 而 Value 則用來
傳回
讀取之資料,若呼叫成功, 此一函數將傳回 True。假設我們想讀取 
"HKEY_CLASSES_R
OOT\.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"
 Sub
key 之下名稱為 "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" 的
 Val
ue, 呼叫的過程如下:
 
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 所讀取的
資料除
在於: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 陣列
您可能覺得奇怪, vType 含有以上幾種類型, 為什麼讀取的資料一律放在 
Byte 陣列
中(bArr 參數), 這是因為 Windows API 的設計使然, 為了將 Byte 陣列轉換成
不同
型別的資料,筆者又撰寫了以下的幾個函數:
 
資料類型 意義
ByteArrayToString 將 Byte 陣列轉換成字串
ByteArrayToMultiString 將 Byte 陣列轉換成多重字串
ByteArrayToLong 將 Byte 陣列轉換成長整數
 
使用以上的轉換函數以前, 讓筆者先說明 vType 幾種資料類型的意義:
 
(1) REG_SZ:一般的字串, 若 vType 等於此一類型, 則呼叫 
ByteArrayToString 進
行轉換。
(2) REG_EXPAND_SZ:也是呼叫 ByteArrayToString 進行轉換, 但此類字串中含
有 %W
inDir% 之類的字串(註:%WinDir% 表示 Windows 的所在目錄), 遇到此類字串,
應該
再呼叫 ExpandEnvironmentStrings API 函數將字串展開, 舉例來說, 假設 
Windows
 的所在目錄是 "C:\Windows", 則 "LoadHigh %WINDIR%\Command\DOSKey" 經過
 Expa
ndEnvironmentStrings API 的轉換之後, 將成為 "LoadHigh C:
Windows\Command\DOS
Key"。(特別說明:有些程式雖然寫入字串資料到登錄資料庫時,所設定的資料類
型為
REG_SZ, 但字串中仍然含有 %WinDir% 之類的字串, 因此若不能確定讀取的字串
中是
否含有 %WinDir% 之類的字串, 則一律呼叫 ExpandEnvironmentStrings 是比較
保險的
)
(3) REG_MULTI_SZ:多重字串, 其結構如圖-4, 此類資料, 若呼叫 
ByteArrayToStr
(3) REG_MULTI_SZ:多重字串, 其結構如圖-4, 此類資料, 若呼叫 
ByteArrayToStr
ing 進行轉換, 則得到如圖-4 的字串, 為了轉換此類字串, 筆者提供的副程式
是 B
yteArrayToMultiString, 呼叫前只要宣告一個空的字串陣列, 例如 Dim S() As
 Str
ing, 則轉換之後, S 就等於含有多個字串的陣列了。
 
圖-4 多重字串(REG_MULTI_SZ) 的意義
 
(4) REG_DWORD:表示含有 4 個字元的數值, 可呼叫 ByteArrayToLong 進行轉換

(5) REG_DWORD_BIG_ENDIAN:不同 CPU 對於數值的安排方式並不相同,所謂 
Big Endi
an 指的是高位元組放在前面的安排方式(註:Intel 的 CPU 是低位元組放在前面
,另外
請注意 Windows NT 可能在非 Intel 的 CPU 底下執行), 雖然此一類型的位元組
安排
順序有點不同,但是對程式而言, 欲取得正確的長整數, 也是呼叫 
ByteArrayToLong
 進行轉換。
(6) REG_BINARY:Binary 資料, 使用 Byte Array 表示最為恰當,所以不必轉換

 
假設我們利用 GetValue 讀取 Value 之後, 想將它顯示出來, 則以下是參考範
例:
 
ret = GetValue(hKey, Name, bArr, vType)
Debug.Print ValueOutput( bArr, vType ) ' 將讀取的資料顯示出來
 
Function ValueOutput(bArr() As Byte, ByVal vType As Long) As String
Dim S As String, S2 As String, length As Integer, L As Long
Dim i As Integer, sArr() As String
Dim i As Integer, sArr() As String
Select Case vType
Case REG_SZ, REG_EXPAND_SZ
ByteArrayToString bArr, S
' 呼叫 ExpandEnvironmentStrings
S2 = String(Len(S) + 256, Chr(0))
length = ExpandEnvironmentStrings(S, S2, Len(S2))
S = Left(S2, length - 1)
ValueOutput = "Type=String, Data=" & S
Case REG_MULTI_SZ
ByteArrayToMultiString bArr, sArr
ValueOutput = "Type=MultiString, Data="
For i = LBound(sArr) To UBound(sArr)
ValueOutput = ValueOutput & sArr(i) & ", "
Next i
Case REG_DWORD, REG_DWORD_BIG_ENDIAN
ByteArrayToLong bArr, L
ValueOutput = "Type=Long, Data=" & L
Case REG_BINARY
ValueOutput = "Type=Byte Array, Data="
For i = LBound(bArr) To UBound(bArr)
ValueOutput = ValueOutput + Format(Hex(bArr(i)), "00")
Next i
Next i
End Select
End Function
 
◆ GetValueByIndex 函數:讀取任意Value
 
GetDefaultValue 可讀取 Default Value, GetValue 可讀取特定名稱的 Value,
但如
果我們不知道 Value 的名稱而想讀取某一 Key(或 Subkey) 的所有 Value(包含 
Defau
lt Value), 則必須使用 GetValueByIndex。
 
GetValueByIndex 的函數定義是:
 
Function GetValueByIndex(ByVal hKey As Long, ByVal Index As Long, Name 
As St
ring, Value() As Byte, vType As Long) As Boolean
 
與 GetValue 不同的是:GetValue 需傳入 ValueName 參數, 而 
GetValueByIndex 則
是傳入 Index, Index 的值是 0~N, 若要讀取所有的 Value, 則程式大致如下

 
Index = 0
Do
ret = GetValueByIndex( hKey, Index, Name, bArr, vType )
Index = Index + 1
Loop Until Not ret
Loop Until Not ret
 
也就是從 0、1、2…開始讀取 Value 的內容, 直到 GetValueByIndex 傳回 
False 時
, 即表示讀取了所有的 Value。
 
就像 GetValue 會傳回 Byte 陣列(Value() As Byte 參數) 及資料類型(vType As
 Lon
g 參數)一樣, GetValueByIndex 也會傳回 Byte 陣列及資料類型, 但重要的是
 GetV
alueByIndex 還會傳回 Value 的名稱(Name 參數), 而為了區分 
GetValueByIndex 所
讀取的是 Default Value 還是特定名稱的 Value(註:Default Value 的 Index 
不一定
等於 0), 程式必須判斷讀回的 Name 參數, 若 Name 參數等於 "",則表示此一
 Ind
ex 為 Default Value。以下是利用 GetValueByIndex 讀取某一 Key(或 
Subkey) 所有
 Value 並且加以顯示的範例程式:
 
Dim hKey As Long, Index As Long, Name As String
Dim bArr() As Byte, vType As Long, ret As Boolean
Index = 0
Do
ret = GetValueByIndex( hKey, Index, Name, bArr, vType )
If ret Then
If Name = "" Then Name = "(預設值)"
Debug.Print "Name=" & Name & ", " & ValueOutput( bArr, vType )
Index = Index + 1
End If
End If
Loop Until Not ret
 
◆ SetDefaultValue 函數:寫入Default Value
 
SetDefaultValue 是與 GetDefaultValue 相對應的函數, 前者用來寫入資料,後
者則
是讀取資料, 兩者除了讀取與寫入的差別之外, 使用方法與參數定義則完全相同

 
比較值得注意的事情是, 當我們想寫入某一個 Subkey 的 Default Value 時,若
此一
 Subkey 不存在, 則 Windows 會自動建立此一 Subkey, 然後才寫入 Default 
Value
, 假設 "HKEY_LOCAL_MACHINE\SOFTWARE\kj\Registry" Subkey 並不存在, 則以
下敘
述:
 
ret = SetDefaultValue(HKEY_LOCAL_MACHINE, "SOFTWARE\kj\Registry", "kj 
Regist
ry Master")
 
會先建立以下兩個 Subkey:(HKEY_LOCAL_MACHINE\SOFTWARE 為已存在的 
Subkey)
 
HKEY_LOCAL_MACHINE\SOFTWARE\kj
HKEY_LOCAL_MACHINE\SOFTWARE\kj\Registry
 
然後才寫入 "kj Registry Master" 到 
"HKEY_LOCAL_MACHINE\SOFTWARE\kj\Registry"
 Subkey 的 Default Value。
 Subkey 的 Default Value。
 
◆ SetValue 函數:寫入特定Value
 
SetDefaultValue 用來寫入 Default Value, 而 SetValue 則用來寫入特定名稱
的 Va
lue, 它的函數定義如下:
 
Function SetValue(ByVal hKey As Long, ByVal ValueName As String, ByVal 
vType
 As Long, Value As Variant, Optional ByVal lenValue As Integer) As 
Boolean
 
比較特別的參數是 Value 及 lenValue, 其中 Value 參數所定義的型別是 
Variant(不
定型),表示可以接受任何型別的資料, 而 lenValue 參數則以 Optional 的方式
來宣
告,表示可以省略。在使用上, 如果呼叫 SetValue 時所設定的是長整數或字串
資料,
則 lenValue 參數可以省略, 因為長整數與字串都可以由程式自己取得長度(長整
數的
長度是 4, 字串的長度則是利用 Len(字串) 來取得), 因此只有設定 
REG_BINARY 類
型的資料時,才需要設定 lenValue 參數, 以下是幾種典型的呼叫方式:
 
' REG_SZ 類型資料, Value 參數傳入字串
ret = SetValue(hKey, "String", REG_SZ, "String Data")
 
' REG_DWORD 類型資料, Value 參數傳入長整數
ret = SetValue(hKey, "Dword", REG_DWORD, 99999)
 
 
' 若是 REG_MULTI_SZ 類型資料, 需使用 Chr(0) 將多個字串串起來
ret = SetValue(hKey, "MultiString", REG_MULTI_SZ, 
"Str1"+Chr(0)+"Str2"+Chr(0
) )
' 以上三種呼叫方式均省略了 lenValue 參數
 
' REG_BINARY 類型資料, Value 參數應傳入 Byte 陣列, lenValue 參數則傳入
資料
長度
Dim bArr(0 To 20) As Byte
ret = SetValue(hKey, "Binary", REG_BINARY, bArr, 21)
 
◆ RegDeleteValue API:刪除特定 Value
 
除了筆者所提供的 Value 存取函數之外, 您可能還需要使用 API 函數中的 
RegDelet
eValue 來刪除 Value, 此一函數很容易使用, 所以筆者不再提供封裝版的 VB 
函數,
 它含有兩個參數, 意義如下:
 
(1) ByVal hKey As Long:Key Handle。
(2) ByVal lpValueName As String:Value 的名稱, 若傳入 "",則表示刪除 
Defaul
t Value 的內容。
 
若呼叫成功, 則函數傳回 0, 與其他登錄資料庫 API 的慣例相同。
 
 
存取 Value 的範例
------------------------------------------------------------------------
----
----
 
本段落最後讓我們來參考一個比較完整的範例程式, 請開啟下載檔案中的 
value.vbp
專案, 此一程式啟動時, 會在 "HKEY_LOCAL_MACHINE\SOFTWARE\kj\Registry" 
Subke
y 底下寫入以下 Value:
 
資料類型 名稱 資料
  (預設值) kj Registry Master
REG_SZ StringData 這是字串
REG_MULTI_SZ MultiString 字串一(0) +字串二+Chr(0) +Chr(0)
REG_DWORD LongData 99999
REG_BINARY BinaryData 11 22 33 44 AA BB CC DD
 
此時會使用到 SetDefaultValue 及 SetValue 函數, 接著當您按下「顯示所有 
Value
」命令鈕時, 如圖-5, 程式會讀出來所有 Value 並且顯示在 ListBox 之中,此
時會
呼叫 GetValueByIndex 函數。
 
圖-5 存取 Value 的範例:value.vbp
 
最後當程式結束時, 則會刪除以上所有的 Value, 此時會呼叫 GetValueByIndex
 函數
最後當程式結束時, 則會刪除以上所有的 Value, 此時會呼叫 GetValueByIndex
 函數
及 RegDeleteValue API 函數, 完整的程式如下:
 
Private Sub Form_Load()
Dim hKey As Long, ret As Long
ret = SetDefaultValue(HKEY_LOCAL_MACHINE, "SOFTWARE\kj\Registry", "kj 
Regist
ry Master")
ret = RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\kj\Registry", hKey)
ret = SetValue(hKey, "StringData", REG_SZ, "這是字串")
ret = SetValue(hKey, "MultiString", REG_MULTI_SZ, "字串一" + Chr(0) + "
字串二
" + Chr(0))
ret = SetValue(hKey, "LongData", REG_DWORD, 99999)
ret = SetValue(hKey, "BinaryData", REG_BINARY, Array(&H11, &H22, &H33, 
&H44,
 &HAA, &HBB, &HCC, &HDD), 8)
Call RegCloseKey(hKey)
MsgBox "已寫入資料到登錄資料庫中,您可以開啟 RegEdit 加以檢查!"
End Sub
 
Private Sub Command1_Click() ' 顯示所有 Value
Dim Index As Long, ret As Long, hKey As Long
Dim bArr() As Byte, Name As String, vType As Long
ret = RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\kj\Registry", hKey)
ret = GetValueByIndex(hKey, Index, Name, bArr, vType)
ret = GetValueByIndex(hKey, Index, Name, bArr, vType)
While ret
If Len(Name) = 0 Then Name = "(預設 值)"
List1.AddItem Name & vbTab & ValueOutput(bArr, vType)
Index = Index + 1
ret = GetValueByIndex(hKey, Index, Name, bArr, vType)
Wend
Call RegCloseKey(hKey)
End Sub
 
Private Sub Form_Unload(Cancel As Integer)
Dim Index As Long, ret As Long, hKey As Long
Dim bArr() As Byte, Name As String, vType As Long
ret = RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\kj\Registry", hKey)
ret = GetValueByIndex(hKey, Index, Name, bArr, vType)
While ret
Call RegDeleteValue(hKey, Name)
' 不可以執行 Index = Index + 1,因為 Index = 0 的 Value 已刪除,
' 後面的 Index 向前遞減, 所以 Index = 0 又可以讀到 Value,
' 其實在這一個 While 迴圈中, 您可以將 Index 變數改成 0
ret = GetValueByIndex(hKey, Index, Name, bArr, vType)
Wend
Call RegCloseKey(hKey)
Call RegCloseKey(hKey)
MsgBox "kj\Registry 的 Value 已刪除, 利用 RegEdit 檢查時, 記得要先執行
功能
的「檢視/重新整理」!"
End Sub
 
------------------------------------------------------------------------
----
----
Key 的存取
------------------------------------------------------------------------
----
----
 
相對於 Value 的存取, Key 的存取要簡單得多, Windows 所提供的 API 函數有
 Reg
CreateKey(建立 Subkey)、RegEnumKey(逐一列舉 Subkey)、RegDeleteKey(刪除 
Key 或
 Subkey), 而筆者也仿效 Value 的存取函數, 提供了 GetSubkeyByIndex 替代
比較不
容易使用的 RegEnumKey, 至於 RegCreateKey 及 RegDeleteKey, 由於比較簡單
,所
以直接呼叫即可, 以下就讓筆者來說明這幾個函數的用法。
 
RegCreateKey API 函數:建立 Subkey
------------------------------------------------------------------------
----
----
 
RegCreateKey 的用法與 RegOpenKey 完全相同, 所不同的是 RegOpenKey 只能開
啟既
有的 Subkey, 而 RegCreateKey 則可以建立 Subkey, 比較特別的是, 如果呼
叫 Re
有的 Subkey, 而 RegCreateKey 則可以建立 Subkey, 比較特別的是, 如果呼
叫 Re
gCreateKey 所建立的 Subkey 是一個已存在的 Subkey, 則 RegCreateKey 的作
用與
RegOpenKey 相同, 由於 RegCreateKey 具有以上的特性, 很多人乾脆就不用 
RegOpe
nKey 了,而不管開啟或建立 Subkey, 都一概使用 RegCreateKey。
 
GetSubkeyByIndex 函數:讀取任意 Subkey
------------------------------------------------------------------------
----
----
 
此一函數的定義如下:
 
Function GetSubkeyByIndex(ByVal hKey As Long, ByVal Index As Long, 
KeyName A
s String) As Boolean
 
如果已經瞭解之前存取 Value 的 GetValueByIndex 函數, 應該不難瞭解此一函
數的用
法,假設我們想列舉 "HKEY_LOCAL_MACHINESOFTWARE\Microsoft" 的所有 Subkey
, 則
程式大致如下:
 
Dim ret As Long, hKey As Long, Index As Integer, Name As String
ret = RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\Microsoft", hKey)
While GetSubkeyByIndex(hKey, Index, Name)
Debug.Print Name ' 印出 Subkey Name
Index = Index + 1
Index = Index + 1
Wend
 
RegDeleteKey API:刪除 Key 或 Subkey
------------------------------------------------------------------------
----
----
 
RegDeleteKey 函數含有兩個參數:
 
(1) ByVal hKey As Long:Key Handle。
(2) ByVal lpSubkey As String:Subkey 的字串, 若傳入 "",表示刪除 Key。

 
請注意當我們利用 RegDeleteKey 刪除某一個含有 Subkey 的 Key 或 Subkey 時
(假設
 "HKEY_LOCAL_MACHINE\kj" 含有 "Registry" Subkey,則刪除 
"HKEY_LOCAL_MACHINE\
kj" 即屬於此一情況), 對於 Windows 95 或 NT 來說, 結果是不相同的, 
Windows
95 會將此一 Key(或 Subkey) 及其所有 Subkey 全數刪除, 而 Windows NT 則傳
回失
敗值。
 
為了能夠讓 Windows NT 也具備 Windows 95 刪除所有 Subkey 的功能, 筆者提
供了另
一個 VB 函數:
 
Function DeleteSubkeyTree(ByVal hKey As Long, ByVal Subkey As String) As
 Boo
lean
lean
 
此一函數會呼叫 GetSubkeyByIndex 再向下讀取所有的 Subkey, 然後一一將它們
刪除
,程式的內容如下:
 
Function DeleteSubkeyTree(ByVal hKey As Long, ByVal Subkey As String) As
 Boo
lean
Dim ret As Long, Index As Long, Name As String
Dim hSubKey As Long
ret = RegOpenKey(hKey, Subkey, hSubKey) ' 取得 Subkey 的 Handle
If ret <> 0 Then
DeleteSubkeyTree = False
Exit Function
End If
ret = RegDeleteKey(hSubKey, "")
If ret <> 0 Then ' 失敗,表示此一 Subkey 的下一層還有 Subkey
While GetSubkeyByIndex(hSubKey, 0, Name) And _
DeleteSubkeyTree(hSubKey, Name) ' 遞迴刪除 Subkey 的 Subkey
Wend
ret = RegDeleteKey(hSubKey, "")
End If
DeleteSubkeyTree = (ret = 0)
End Function
End Function
 
以上程式在 While 迴圈中利用了遞迴呼叫(在 DeleteSubkeyTree 函數中又呼叫 
Delet
eSubkeyTree)的特性, 很輕易地就可以向下刪除所有層次的 Subkey。
 
------------------------------------------------------------------------
----
----
登錄資料庫的應用
------------------------------------------------------------------------
----
----
 
由於登錄資料庫是 Windows 與應用程式存放常用資料的大本營, 同時也會影響 
Windo
ws 及應用程式的運作模式, 因此直接存取登錄資料庫, 猶如通往 Windows 及應
用程
式核心的直達車, 確實是一件很過癮的事情, 但如果胡亂寫入資料或更改資料,
卻可
能造成 Windows 及應用程式執行的錯誤。
 
設定應用程式的路徑
------------------------------------------------------------------------
----
----
 
在 DOS 底下, 我們會利用 PATH 環境變數來設定程式的路徑, 而當我們輸入某
一執行
檔名時, DOS 便會從目前工作目錄及設定於 PATH 環境變數中的路徑來尋找執行
檔。
 
 
對 Windows 而言, 搜尋執行檔的順序是:(1) 目前工作目錄 (2) Windows 的所
在目錄
及 System 目錄 (3) 設定於 PATH 環境變數中的路徑。如果我們想讓使用者直接
在「執
行」交談窗輸入檔名(不必輸入完整路徑)就可以啟動程式,比較普通的方法是將程
式的
執行檔複製到 Windows 的所在目錄(或 System 目錄),或者設定好 PATH 環境變
數,
不過這兩種方法都有缺點, 將執行檔複製到 Windows 的所在目錄, 會造成 
Windows
所在目錄的檔案越來越多, 而不易於管理, 設定 PATH 環境變數則必須重新啟動
電腦
才能生效, 且環境變數的空間是有限的。
 
其實 Windows 搜尋執行檔的方法除了以上的 (1)(2)(3) 之外, 還會搜尋登錄資
料庫
"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths"
 之下
的 Subkey, 假設我們在此一 Subkey 之下, 建立了名稱為 "Project1.exe" 而
 Defa
ult Value 等於 "c:\vb\myapp\Project1.exe" 的 Subkey,則將來我們在「執行
」交談
窗中輸入 "Project1" 之後, Windows 就會搜尋到 "c:\vb\myapp\Project1.exe"
 的執
行檔完整路徑而啟動此一程式, 您可以利用 RegEdit 觀察 
""HKEY_LOCAL_MACHINE\SO
FTWARE\Microsoft\Windows\CurrentVersion\App Paths" 的內容, 即可瞭解其中
的端
倪。
 
改變控制台的設定值
------------------------------------------------------------------------
----
----
 
使用 Windows 時, 我們可能會使用「控制台」來改變系統的設定值, 此時,系
統的設
使用 Windows 時, 我們可能會使用「控制台」來改變系統的設定值, 此時,系
統的設
定值是存放在 "HKEY_CURRENT_USER\Control Panel" 之下,舉例來說, 利用「控
制台
」改變桌面的底圖, 將會改變 "Desktop" Subkey 的 Wallpaper 設定值, 反過
來說,
 如果改變 "HKEY_CURRENT_USER\Control Panel\Desktop" 的 Wallpaper 設定值
, 也
應該可以改變桌面的底圖, 基本上,這句話是正確的, 但筆者要特別說明的是,
 當我
們改變登錄資料庫的內容時, Windows 或應用程式並不知道登錄資料庫的內容已
經改變
了, 因此不一定會立刻反映出來,而通常要等到下次開機, Windows 或應用程式
重新
讀取登錄資料庫時才有作用。(註:有關「控制台」相關的設定, Windows 提供了
另一
個 API 函數─SystemParametersInfo, 呼叫此一函數除了改變登錄資料庫之外,
也會
通知 Windows 立刻反應結果)。
 
副檔名與應用程式的啟動
------------------------------------------------------------------------
----
----
 
在檔案總管裡面, 如果我們雙按 .txt 的檔案, 檔案總管會啟動「記事本」開啟
此一
檔案,如果雙按 .bmp 的檔案, 則會啟動「小畫家」開啟此一檔案…, 此一工作
模式
,也與登錄資料庫有關, 以下就讓筆者以 ".txt" 為例, 來說明登錄資料庫中的
相關
設定。
 
首先請檢視 HKEY_CLASSES_ROOT 之下的 ".txt" Subkey, 它的 Default Value 
等於
"txtfile", 接著繼續在 HKEY_CLASSES_ROOT 之下尋找此一內容("txtfile")的 
Subke
y, 果然可以找到, 而它的 Default Value 等於 "純文字文件",接著再檢視 
"txtfi
y, 果然可以找到, 而它的 Default Value 等於 "純文字文件",接著再檢視 
"txtfi
le" 之下的內容, 以筆者機器為例, 結構如下:
 
txtfile: (預設值)="純文字文件"
shell: (預設值)=""
open: (預設值)=""
command: (預設值)="C:\\WINDOWS\\NOTEPAD.EXE %1"
print: (預設值)=""
command: (預設值)="C:\\WINDOWS\\NOTEPAD.EXE /p %1"
 
由於以上的結構, 使得我們在 ".txt" 的檔案上面按下滑鼠右鈕時,所有出現的
快顯功
能表含有「開啟舊檔」及「列印」兩種命令, 如圖-6:
 
圖-6 因為 txtfile\shell 之下含有 open 及 print, 所以檔案總管的快顯功能
表會出
現「開啟舊檔」與「列印」兩種命令
 
接著請看 "open\command" 的 Default Value(預設值), 它等於 "C:
\WINDOWS\NOTEPA
D.EXE %1", 這表示當我們選取「開啟舊檔」命令時,檔案總管會將其中的 %1 替
換成
 .txt 檔案名(假設是 abc.txt), 然後執行 "C:\WINDOWS\NOTEPAD.EXE abc.
txt", 同
樣的, 由於 "print\command" 的 Default Value(預設值) 等於 "C:
\WINDOWS\NOTEPA
D.EXE /p %1", 當我們選取「列印」命令時,檔案總管的所執行的命令是 "C:
\WINDOW
S\NOTEPAD.EXE /p abc.txt"。
 
 
在圖-6 的快顯功能表中, 「開啟舊檔」是以粗體顯示, 這表示當我們雙按 ".
txt" 檔
案時, 所啟動的命令是「開啟舊檔」, 在此筆者想問的是,為什麼雙按啟動的是
「開
啟舊檔」, 而不是「列印」, 其實這跟 "txtfile\shell" 的 Default Value 有
關,
 由於 "txtfile\shell" 的沒有 Default Value,所以檔案總管便以第一個 
Subkey(=o
pen)為雙按時的命令, 如果我們將 "txtfile\shell" 的 Default Value 改成 
"print
", 則雙按所執行的命令將變成「列印」。
 
瞭解其他設定的意義
------------------------------------------------------------------------
----
----
 
筆者以上所舉的只是少數的幾個例子, 瞭解越多 Key 及 Value 的意義, 將越懂
得藉
助登錄資料庫的設定來控制 Windows 及應用程式的行為, 本文受限於篇幅, 暫
且介紹
至此, 如果您有興趣進一步瞭解登錄資料庫內各種 Key 及 Value 的意義, 坊間
可以
找到不少中英文的參考書。
 
一個很困難的問題, 及一個很簡單的解決方案
------------------------------------------------------------------------
----
----
 
有不少讀者問:「不同程式之間, 如何共用資料?」, 這個問題真的很困難,對
 Win
dows 3.1 而言, 記憶體的定址空間對所有程式而言是共用的, 為了達到資料的
共用,
dows 3.1 而言, 記憶體的定址空間對所有程式而言是共用的, 為了達到資料的
共用,
可以由程式 A 配置資料, 然後傳遞資料的位址給程式 B, 程式 B 便可以透過此
一位
址使用此一資料,而達到共用資料的目的, 這一種共用資料的特色是每一程式都
可以存
取記憶體中任何位址的資料,不管這份資料是否屬於自己所有, 乍看之下, 此一
存取
資料的模式十分方便,但也正因為如此, 很容易破壞他人的程式碼造成當機, 而
這正
是 Windows 3.1 最為人詬病的一件事情。
 
對 32-bits 的 Windows(Windows 95 或 NT)而言, 每個程式都擁有獨立的 2GB 
定址空
間, 首先讓筆者來解釋何謂獨立的定址空間, 假設程式 A 某一資料的位址是 
&H1234
0, 而資料的內容是 "AAAA", 但是對程式 B 而言, 同樣的 &H12340 位址卻可
能存放
著 "BBBB" 的資料, 雖然位址相同, 但實際上,在 Windows 的巧妙安排下, 使
用的
卻是不同的記憶體位置, 為什麼要這麼做呢?道理很簡單,也許程式 A 有 bug 
把資料
寫錯了位置, 如果它的定址空間不是獨立的, 便可能破壞其他程式的程式碼或資
料,
而造成其他程式的錯誤, 而相反的, 若每一個程式的定址空間都是獨立的, 寫
得再爛
的程式也不會毒到別人的程式,如此一來, 便可以讓 Windows 更趨於穩定。
 
但有一得, 卻也有一失, 由於每個程式的定址空間是獨立的, 便造成了程式之
間共用
資料的困難,為了共用資料, 最普遍的解決方式是利用「記憶體對映檔案」
(Memory M
apped File)或是 ActiveX EXE 來達到資料共用的目的, 但這兩種解決方案都不
算簡單
, 而本期我們介紹的登錄資料庫正是一個簡單的資料共用方法,只要程式之間講
好資料
存放在哪一個 Key 及 Value 之下, 就可以利用本文所介紹的函數存取資料而達
到共用
的目的。

0, 而資料的內容是 "AAAA", 但是對程式 B 而言, 同樣的 &H12340 位址卻可
能存放
--
" The Matrix is everywhere, it's all around us, here even in this room.
 You
 can see it out your window, or  on your television. You feel it when 
you
 go to work, or go to church or pay your taxes. It is the world that has
 been
 pulled over your eyes to blind you from the truth... Unfortunately, 
no one
can be told what the Matrix is. You have to see it for yourself."
                                                                   
Morphe

※ 来源:.武汉白云黄鹤站 bbs.whnet.edu.cn.[FROM: 202.114.3.124]

--
我并不是在等待奇迹,因为我知道没有奇迹的。
有的,也只是爱情、意志和勇气。
是这些东西的重叠后,而成为奇迹的。
所以,我从未曾想过放弃。

※ 修改:·Love1976 於 Apr  6 04:25:06 修改本文·[FROM: 202.112.140.138]

--
☆ 来源:.哈工大紫丁香 bbs.hit.edu.cn.[FROM: blo0m.bbs@smth.org]
--
※ 转载:.哈工大紫丁香 bbs.hit.edu.cn.[FROM: 202.118.247.254]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:416.725毫秒