第 31 章 登錄資料庫 ( Registry )


機碼、子機碼與數值

登錄資料庫英文稱為 registry,它是 Windows 作業系統堣@個很複雜的資料庫,它所保存的資料包含硬體設備資料、應用軟體所設定的資料、使用者使用偏好的資料、作業系統設定的資料等等。例如當使用者安裝新軟體時,這個新軟體就有可能在登錄資料庫堨[上安裝的目錄、預設的程式、如何移除等資料。當使用者改變操作設定,例如在光碟機中放入光碟片不自動播放。這些都會記錄在登錄資料庫堙C觀察登錄資料庫最常用的方法就是使用「RegEdit」程式 ( RegEdit 稱為登錄編輯程式 ),您可以在鍵盤上先按下「Win」鍵不放,然後按下「R」鍵,再於跳出來的對話盒中輸入「RegEdit」( 不分大小寫,也可以在「命令提示字元」內輸入 RegEdit ),就能看見下面的視窗,小木偶是在 Windows XP 系統內執行的,其他版的 Windows,也應該是大同小異。

登錄編輯程式

您仔細觀察,可以發現登錄資料庫的結構屬於樹狀結構,最上一層有五個項目,分別是 HKEY_CLASSES_ROOT、HKEY_CURRENT_USER、HKEY_LOCAL_MACHINE、HKEY_USERS、HKEY_CURRENT_CONFIG,稱之為「機碼」( key,大陸翻成「鍵」),換句話說,最上面一層有五個機碼。不過這是因為 Windows XP 只有五個機碼,其他系統未必如此,而這最上一層的機碼又都有縮寫,以及固定的代碼。下表是 Windows 系統使用的最上層機碼及其用途:

機碼縮寫機碼代碼備註
HKEY_CLASSES_ROOTHKCR80000000H HKEY_CLASSES_ROOT 堶悸漱l機碼,記載著各種檔案類型不同的副檔名,例如副檔名為「.txt」的檔案,就被定義成「文字文件」,並且記錄著用哪種應用程式來開啟此類檔案,當使用者以滑鼠雙擊這類檔案時,就會直接用該種應程式來開啟。
HKEY_CURRENT_USERHKCU80000001H HKEY_CURRENT_USER 機碼是從 HKEY_USERS 衍伸出來的,它記載著目前使用者的資料、對作業系統的設定值。也包含個人化作業環境的設定資料,因此許多設定都會落在這個機碼中。
HKEY_LOCAL_MACHINEHKLM80000002H HKEY_LOCAL_MACHlNE 記載系統中各種硬體設定的資料。包括 BIOS,硬體周邊、印表機、光碟機等等的硬體相關資料設定。
HKEY_USERSHKU80000003H HKEY_USERS 機碼記載著每個使用者的設定資料。
HKEY_PERFORMANCE_DATAHKPD80000004H Windows NT/2000/XP 等系統才有,功能和 Windows 9x 的 HKEY_DYN_DATA 類似,但用 RegEdit 無法觀察到。
HKEY_CURRENT_CONFIGHKCC80000005H HKEY_CURRENT_CONFIG 機碼是記載目前使用者對於硬體的設定檔。
HKEY_DYN_DATAHKDD80000006H 此機碼只有 Windows 95/98/98 SE/ME 系統才有,用於儲存作業系統的動態資料,例如效能統計等。底下有兩個子機碼:ConfigManager 和 PerfStats。

最上面一層的機碼,都可以按下前面的展開來,展開後的下一層稱為「子機碼」( subkey,大陸翻成「子鍵」)。有些子機碼之前有按鈕的,還可以再展開來,甚至有許多層,為了單純,這些再展開及其以後展開的項目也都稱為子機碼。例如 HKEY_CURRENT_CONFIG 機碼展開後,有兩個子機碼,分別是「Software」、「System」,而 Software 子機碼再展開後只有一個子機碼,Fonts。有些子機碼不能再展開,像 Fonts 就是不能展開的子機碼,其前面沒有按鈕。習慣上,子機碼的表示法採用類似子目錄的方式表示,例如上面的 Fonts 可以表示成「HKEY_CURRENT_CONFIG\Software\Fonts」,要注意 HKEY_CURRENT_CONFIG 前不要加上「\」,因為 HKEY_CURRENT_CONFIG 已經是最上層了。當然,也有機碼不能展開,那就表示該機碼是最下一層的機碼,這些機碼和子機碼的上、下層關係,都會在登錄編輯程式左邊的子視窗,以樹狀結構顯示。

每個機碼或子機碼,不僅僅可能有數量不同的子機碼,還會有一些數值 ( value,大陸翻成「鍵值」 ),至少,每個機碼或子機碼都會至少有一個稱為「預設值」的「數值」。有些子機碼可能不只一個數值,這些數值都會在登錄編輯程式右邊的子視窗顯示出來。每個數值都包含「名稱」與「資料」兩部份。例如上面的 Fonts 子機碼就有兩個數值,一是以「預設值」為名稱、以「數值未設定」為資料的數值;另一個是以「LogPixels」為名稱、以「60H」為資料的數值。數值雖名曰數值,但其資料卻不一定是數字,有可能是字串,也有可能是其他類型。底下的表格,是數值的資料類型,請參考:

鍵值的資料類型數值說明
REG_NONE0不含特定類型的資料
REG_SZ1以 0 結尾的字串
REG_EXPAND_SZ2 可展開的字串,表示此類字串包含有類似「%WinDis%」之類的字串。碰到 REG_EXPAND_SZ 時,可以呼叫 ExpandEnvironmentStrings API 展開這些字串。( 註一 )
REG_BINARY3 二進位資料,大多數的硬體元件資料會儲存為二進位資料,並在「登錄編輯程式」中以十六進位的格式顯示
REG_DWORD432 位元整數,許多裝置驅動程式及服務的參數都屬於這個類型,並且會在「登錄編輯程式」中以二進位、十六進位或十進位的格式顯示
REG_DWORD_BIG_ENDIAN532 位元的 Big Endian 長整數
REG_LINK6可以使某個機碼指向另一個機碼,這兩個機碼好像是連接起來,MSDN 建議一般程式不應該使用這種資料類型
REG_MULTI_SZ7多重字串,兩字串之間以 0 分開,最後一個字串之後有兩個 0
REG_RESOURCE_LIST8 用於硬體驅動程式或控制某個周邊設備所使用的資源列表,通常是一串二進位數值

數值名稱的長度是有限制的,和 Windows 的版本有關。另外,機碼中所有數值名稱長度總和,不可超過 64K 位元組。各版本的 Windows 數值長度限制見下表:

登錄資料庫所儲存的資料,並非僅僅一個檔案,而是許多檔案分門別類儲存起來,這些檔案稱為「registry hive files」。以 Windows 95 為核心的 Windows 95/98/98SE/Me 和以 Windows NT 為核心的 Windows 2000/XP/Vista/8/8.1/10 儲存方式不同。在 Windows 98 中,登錄檔案會存在 User.dat 及 System.dat 堙F在 Windows Me 中,登錄檔案則是命名為 Classes.dat、User.dat 及 System.dat。以 NT 為核心的系統,大部分機碼都存在 %SystemRoot%\System32\config 子目錄堙A只有 HKEY_CURRENT_USER 的資料儲存在 %Userprofile%\NTUSER.DAT 檔案堙C

64 位元 Windows 系統的登錄資料庫

自微軟推出 Windows XP 系統後,個人用途的 Windos Vista/7/8/8.1/10,都可分為 32 位元版與 64 位元版。照理來說,32 位元的 Windows 只能執行 32 位元的應用程式;而 64 位元的 Windows 只能執行 64 位元的應用程式。但是實際情形並非如此,32 位元的 Windows 的確只能執行 32 位元的應用程式,而 64 位元的 Windows 卻能執行 32 位元與 64 位元的應用程式。這是因為 64 位元的 Windows 有一個稱為 WOW64 ( Windows 32-bit on Windows 64-bit ) 的子系統,可以把它想像成模擬器,它專門負責此事。當 32 位元的應用程式在 64 位元的 Windows 執行時,如要呼叫 DLL 時,本來應該到「%SystemRoot%\system32」( 註二 ) 目錄堨h尋找,但在 64 位元的 Windows 中,這兒的 DLL 都是 64 位元的程式碼,故系統會悄悄的把它重新導向到存放 32 位元的 DLL 所在目錄,「%SystemRoot%\SysWOW64」,因此變成呼叫在「%SystemRoot%\SysWOW64」子目錄堛滿B相對應的 DLL,而呼叫者 ( 32 位元的應用程式 ) 卻會以為真的是在 32 位元的 Windows 下執行。故而 64 位元的 Windows 能執行 32 位元和 64 位元的應用程式。

而在 64 位元的 Windows 堙A登錄編輯程式 ( RegEdit.EXE ) 也有 32 位元與 64 位元兩種版本,分別位於「%SystemRoot%\SysWOW64\RegEdit.exe」與「%SystemRoot%\RegEdit.exe」兩子目錄堙A而且名稱都叫「RegEdit」。它們都能夠在 64 位元的 Windows 系統中執行,但不能同時執行,除非其中一個加上「-m」參數。但是 32 位元的 RegEdit 只能讀取或寫入 32 位元的機碼;64 位元的 RegEdit 能同時讀取或寫入 32 位元及 64 位元的機碼。這種情形,也是因為系統在 32 位元的程式讀取或寫入機碼時,作業系統會偷偷地將目標轉向到 32 位元的機碼內。

底下,小木偶要談談 32 位元的機碼與 64 位元的機碼。

事實上,在 64 位元版本的 Windows 系統中,登錄資料庫也分成 32 位元與 64 位元的機碼。許多 32 位元機碼具有與 64 位元的對應機碼相同的名稱,雖然機碼名稱相同,但其數值可能不同。這是因為許多應用程式有 32 位元與 64 位元版的分別,而使用者有可能同時安裝或使用,但其設定的資料可能不同,因此才會在 64 位元的 Windows 中,同時擁有 32 位元與 64 位元的機碼。舉兩個例子來說明,在 64 位元的 Windows 的登錄資料庫堙G

  1. 「HKEY_LOCAL_MACHINE\SOFTWARE\」是保留給 64 位元的應用程式登錄資料之用,如果是 32 位元的應用程式,會被改寫到「HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\」堙C
  2. 「HKEY_CURRENT_USER\Software\」是保留給 64 位元的應用程式登錄資料之用,如果是 32 位元的應用程式,會被改寫到「HKEY_CURRENT_USER\Software\Wow6432Node\」堙C

因此,「HKEY_LOCAL_MACHINE\SOFTWARE\」、「HKEY_CURRENT_USER\Software\」是原本 64 位元應用程式所存取的機碼所在地,稱為 64 位元機碼;而其下的子機碼,「Wow6432Node」,是 32 位元應用程式所存取的機碼所在地,稱為 32 位元機碼。要把登錄的資料寫在哪堙A並不需要應用程式擔心。Windows 系統會自行判斷應用程式是 32 位元還是 64 位元,就能夠自動把登錄的資料寫在正確的機碼下面。


幾個有關登錄資料庫的用途

①開機自動執行的程式

某些程式在 Windows 一開機,就會自動執行,例如「靈格斯翻譯家」、「Avast」、「Lotus QuickStart」等軟體。據小木偶所知,至少有三個方法可以做到:

  1. 工作排定:
  2. 啟動資料夾:把要一開機就執行的程式,建一捷徑,再以滑鼠把剛建好的捷徑拖入「開始」按鈕的啟動資料夾堙C
  3. 修改登錄資料庫:

②預設輸入法

預設輸入法是指 Windows 一啟動,桌面一出現時的輸入法;也是當使用者執行 WORD 等應用程式後,一進入該程式的輸入法。這個輸入法是可以自由選擇的,您可以到「開始」→「控制台」→「日期、時區、語言和區域選項」→「地區及語言選項」→「語言」標籤→「詳細資料」按鈕,就會出現下圖左邊的視窗,您可以摁下「預設輸入語言」內的複合控制項,選擇您想要的預設輸入法。

當然這項選擇的結果,也會保存在資料登錄庫堙C您可以在「HKEY_CURRENT_USER\Keyboard Layout\Preload」機碼中看見好幾個數值。名為「1」的數值所代表的資料,就是預設的輸入法。只不過這些資料,並非以一目了然的方式表示,看起來好像代碼,底下是這些代碼的意義:

輸入法代碼輸入法名稱及意義
00000404美式鍵盤
E0010404注音輸入法
E0020404倉頡輸入法
E0050404行列輸入法
E0080404微軟新注音輸入法
E0090404微軟新倉頡輸入法
E0200404自然輸入/新酷音/漢音輸入法
E0200404 嘸蝦米 E0210404 自然輸入法 E0010411 日文輸入系統

③檔案的關聯性

如果您在 Windows 的檔案總管,以滑鼠雙擊副檔名為 .docx 的檔案,就會執行 Word,並把該檔案載入,以供您編輯;如果您雙擊副檔名是 .txt 的檔案,就會執行小作家,並把載入該檔案,以供您編輯;如果您雙擊副檔名為 .mp3 的檔案,就會執行「Windows Media Player」,並播放該音樂檔。Windows 是怎麼做到這些的?這當然與登錄資料庫有關。

如果展開 HKEY_CLASSES_ROOT 機碼,就會發現底下有許多類似「.docx」、「.txt」、「.mp3」……的子機碼,而這些子機碼又跟副檔名一樣,想必和這事脫離不了關係。小木偶想以副檔名為「.txt」為例子來說明,如果您點選「.txt」子機碼,應該會看見其預設值為「txtfile」。然後您可以在「HKEY_CLASSES_ROOT」找到「txtfile」子機碼,這個子機碼的預設值為「文字文件」。這個預設值會出現在,使用者把滑鼠移到副檔名為「.txt」的檔案上一段時間後所出現的「工具提示」的類型欄位堙C如下圖所示。( 除了預設值外,.txt 可能還會有其他數值,請參考註三 )

「txtfile」堸ㄓF預設值外,還有兩個數值,EditFlags、FriendlyTypeName。

應用程式可以用 EditFlags 的資料來限制使用者對該種副檔名的檔案做某種限制或處置,EditFlags 的資料類型可以是 REG_DWORD 或 REG_BINARY,所代表的意義如下表:

常數數值說明
FTA_Exclude1H可執行檔
FTA_Show2H這類能顯示檔案的類別,例如資料夾,其實它不是檔案關連的類型
FTA_HasExtension4H有副檔名
FTA_NoEdit8H禁止編輯這類檔案
FTA_NoRemove10H禁止刪除這類檔案
FTA_NoNewVerb20H禁止新增彈出選單內的選項
FTA_NoEditVerb40H禁止變更彈出選單內的選項
FTA_NoRemoveVerb80H禁止刪除彈出選單內的選項
FTA_NoEditDesc100H禁止變更檔案類型
FTA_NoEditIcon200H禁止變更檔案圖示
FTA_NoEditDflt400H禁止變更預設的彈出選單
FTA_NoEditVerbCmd800H
FTA_NoEditVerbExe1000H禁止更改彈出選單
FTA_NoDDE2000H禁止變更 DDE ( Dynamic Data Exchange ) 相關設定
FTA_NoEditMIME8000H
FTA_OpenIsSafe10000H開啟此類檔案不會造成危險,亦即此類檔案不可能攜帶病毒
FTA_AlwaysUnsafe20000H下載時,詢問你如何處理的彈出視窗中,「不要再問我」檢驗盒不可關閉
FTA_AlwaysShowExt40000H此類檔案會顯示副檔名
FTA_NoRecentDocs100000H此類檔案不加入「我最近的文件」( Windows XP ) 或「最近的項目」( Windows 7,在 Win 7 堙A最近的項目預設不開啟 )

FriendlyTypeName 和「txtfile」的預設值一樣,都是用來記錄檔案類型,該資料會出現在使用者把滑鼠停留在副檔名是「.txt」的檔案上時,出現在工具提示中的類型欄堙C卻有兩點不同:一是「FriendlyTypeName」可以用可執行檔或 DLL 堛漲r串資源 ( 稱為間接字串 ),也可以直接以字串表示 ( 稱為直接字串 ),而「txtfile」的預設值只能以後者表示。二是「FriendlyTypeName」較「txtfile」的預設值優先,也就是如果沒有「FriendlyTypeName」機碼,系統才會去讀取「txtfile」的預設值。甚至,如果使用者載入含有「FriendlyTypeName」的資料的 .reg 檔案時,RegEdit 也會修改「txtfile」的預設值,使其與「FriendlyTypeName」所代表的資料相同。

在「txtfile」機碼之下,還有兩個子機碼,「DefaultIcon」、「shell」,分別代表在檔案總管的圖示、在檔案總管按滑鼠右鍵出現的選單。如果您繼續開啟「shell」子機碼,還會發現下面其實還有一些子機碼,例如「open」、「print」、「printto」,這些會在使用者對「.txt」檔按下滑鼠右鍵出現的「彈出式選單」堙A請參考上圖。或許您會問,Windows 怎麼知道,使用者雙擊「.txt」檔時,要開啟檔案或是列印檔案呢?這必須看「shell」之預設值,如果是數值未設定,那麼就以第一個子機碼為雙擊後動作;如果「shell」之預設值為某個子機碼名稱,那麼就以該子機碼為雙擊後的動作。執行的動作可以在子機碼的預設值找到。以上圖為例,「shell」之預設值並未設定,因此使用者雙擊「.txt」檔案時,就會執行「notepad.exe」,並開啟雙擊的檔案。

最後整理一下,Windows 處理副檔名的方法是在 HKEY_CLASSES_ROOT 建立兩個子機碼,第一個子機碼是副檔名的名稱,再以其預設值為名,建立另一個子機碼,這個子機碼包含「DefaultIcon」、「shell」兩個子機碼。請參考右圖,小木偶以 Windows 要處理「.asm」副檔名為例子來說明。在 HKEY_CLASSES_ROOT 底下建兩個子機碼,「.asm」、「asmfile」,其預設值分別是「asmfile」及「組合語言原始碼」;也可以在「.asm」底下再建立一名為「PerceivedType」的數值,其資料為「Text」,但此舉並非一定要做。然後在「asmfile」底下再建兩個子機碼,「DefaultIcon」、「shell」,前者預設值為代表「.asm」為副檔名的檔案圖示,後者預設值可以不設定。再於「shell」底下建一名為「edit」的子機碼,其預設值可以不設定。然後在「edit」底下再設一子機碼,「command」,而其預設值是編輯「.asm」檔所需的程式,例如「C:\Program Files\UltraEdit-32\uedit32.exe %1」。

有關 HKEY_CLASSES_ROOT 的補充說明

事實上,前面所說的,都是在 Windows 9x/Me 中的情形。在 Windows 9x/Me 系統堙AHKEY_LOCAL_MACHINE\Software\Classes 底下也有一份和 HKEY_CLASSES_ROOT 底下一模一樣的子機碼。從 Windows 2000 開始,Windows 系統變成是多人多工的系統,因此在 HKEY_CURRENT_USER\Software\Classes 機碼中,包含了一些可以更改的設定,只適用於當前使用者的設定;而在 HKEY_LOCAL_MACHINE\Software\Classes 機碼含有適用於本機電腦上所有使用者的預設設定;HKEY_CLASSES_ROOT 含有的子機碼則是這兩個子機碼所合併的登錄資料庫。因此,我們可以這樣想像,在 Windows 2000 及其以後的系統,有關副檔名設關連設定,可以在三個地方找到:

  1. HKEY_LOCAL_MACHINE\Software\Classes:這台機器上所有使用者的預設設定,意思是當新加入使用者時,該名新進的使用者所使用的檔案關聯就是從這堳貝到 HKEY_CURRENT_USER\Software\Classes 的。如果對此處的機碼修改,會影響這台機器上所有新進使用者的預設設定。
  2. HKEY_CURRENT_USER\Software\Classes:這埵s放著當前使用者所設定的檔案關聯。每個使用者使用習慣不同,因此極有可能某個使用者用「記事本」編輯 .txt 檔,而另一個使用者卻用 UltraEdit-32,所以說此處的設定是會被更改的。如果對此處的機碼修改,只會影響當前使用者的設定。
  3. HKEY_CLASSES_ROOT:此處的檔案關聯設定,是上面兩處拷貝而來的,一般只供讀取,因此最好不要直接修改此處的機碼,除非是 Windows 9x 系統。

如果您將機碼寫入 HKEY_CLASSES_ROOT 下的機碼,系統就會同步更改 HKEY_LOCAL_MACHINE\Software\Classes。如果您將數值寫入 HKEY_CLASSES_ROOT 下的機碼,並且 HKEY_CURRENT_USER\Software\Classes 底下已存有機碼,系統將只會更改 HKEY_CLASSES_ROOT 的資訊,而不會更改 HKEY_LOCAL_MACHINE\Software\Classes 下的資料。

當使用者在「檔案總管」中,以滑鼠雙擊某個檔案時,Windows 會


有關登錄資料庫的 API

如果要處理登錄資料庫內的資料,例如讀取、修改、建立、刪除等等,都必須先取得某個子機碼的代碼,然後才能對該子機碼的數值做處理。事實上,Windows 對每個機碼和子機碼用不同的整數代表,稱為機碼代碼 ( key handle )。最上層的五個機碼代碼是固定的,從 80000000H∼80000004H。至於子機碼,也各有其代碼。而後如果不再需要處理該機碼時,就應將其關閉。獲取機碼代碼、刪除、修改等工作,都有相對應的 Win32 API 可以做到。因此底下要介紹有關登錄資料庫會用的 Win32 API:

Win32 API 名稱說明
RegOpenKeyEx獲得某個機碼的代碼
RegQueryValueEx 獲得某個機碼中的數值資料及資料類型
RegEnumKeyEx列出某個機碼內的所有子機碼
RegEnumValue列出某個機碼內的所有數值
RegQueryInfoKey取得某一機碼之數值個數、子機碼個數、數值名稱最大長度、子機碼最大長度等資料
RegCreateKeyEx建立子機碼
RegCloseKey關閉機碼

在早期版本的 Windows 堙A還可以使用較簡單的 RegOpenKey、RegQueryValue……等 API,但微軟在 MSDN 婸○o是為了維持相容性,不得已才有這些舊的 API,建議程式設計師最好改用新的 API,RegOpenKeyEx、RegQueryValueEx……。另外這些 API 是包含在 ADVAPI32.DLL 堙A因此在組合語言原始碼堶n有底下兩行:

INCLUDE         ADVAPI32.INC
INCLUDELIB      ADVAPI32.LIB

RegOpenKeyEx

獲取某個機碼代碼,其原型是:

LONG WINAPI RegOpenKeyEx(
  __in          HKEY    hKey,
  __in_opt      LPCTSTR lpSubKey,
  __reserved    DWORD   ulOptions,
  __in          REGSAM  samDesired,
  __out         PHKEY   phkResult
);

第一個參數,hKey,是指已開啟的機碼代碼或子機碼代碼,hKey 之值可以由下面幾種情形獲得:

  1. 最上層的機碼代碼:亦即 HKEY_CLASSES_ROOT、HKEY_CURRENT_USER、HKEY_LOCAL_MACHINE、HKEY_USERS、HKEY_CURRENT_CONFIG 等。
  2. 呼叫 RegCreateKeyEx 或 RegOpenKeyEx 之後,系統會把子機碼代碼傳回給這兩個 API 的參數。

第二個參數,lpSubKey,是一字串位址,此字串是要取得子機碼的表示法,並且不區分大小寫。例如「HKEY_CURRENT_CONFIG\Software\Fonts」和「HKEY_CURRENT_CONFIG\SOFTWARE\FONTS」是一樣的意思。小木偶再舉一例,說明如設定字串,例如要取得「HKEY_CURRENT_CONFIG\Software\Fonts」中「Fonts」的子機碼代碼,可以有兩種方法:
 ①hKey 為 HKEY_CURRENT_CONFIG,lpSubKey 為「Software\Fonts」字串之位址;或者是
 ②hKey 為「HKEY_CURRENT_CONFIG\Software」所代表的機碼代碼,並已經開啟了,而 lpSubKey 為「Fonts」字串位址。
如果 lpSubKey 為 0,或指向空字串,那麼 RegOpenKeyEx 會傳回與 hKey 相同的機碼代碼,這樣也算呼叫成功。第三個參數,ulOptions,被保留,必須是 0。第四個參數,samDesired,表示如何處理此機碼,可以是下面的數值:

samDesired數值說明
KEY_QUERY_VALUE1可以獲取機碼資料
KEY_SET_VALUE2可以設定機碼的資料
KEY_CREATE_SUB_KEY4可以建立子機碼
KEY_ENUMERATE_SUB_KEYS8可以列出子機碼
KEY_NOTIFY10H允許變更通知
KEY_CREATE_LINK20H保留給系統使用
KEY_WOW64_64KEY100H使 32 位元或 64 位元的應用程式,能建立或開啟 64 位元的機碼 ( 詳細情形,請參考註二 )
KEY_WOW64_32KEY200H使 32 位元或 64 位元的應用程式,能建立或開啟 32 位元的機碼 ( 詳細情形,請參考註二 )
KEY_WRITE20006H結合 STANDARD_RIGHTS_WRITE、KEY_SET_VALUE 和 KEY_CREATE_SUB_KEY 三個旗標
KEY_READ20019H結合 STANDARD_RIGHTS_READ、KEY_QUERY_VALUE、KEY_ENUMERATE_SUB_KEYS 和 KEY_NOTIFY 四個旗標
KEY_EXECUTE20019H與 KEY_READ 相同
KEY_ALL_ACCESS0F003FH結合了 STANDARD_RIGHTS_REQUIRED、KEY_QUERY_VALUE、KEY_SET_VALUE、KEY_CREATE_SUB_KEY、KEY_ENUMERATE_SUB_KEYS、KEY_NOTIFY 與 KEY_CREATE_LINK 七個旗標

最後一個參數,phkResult,指向一個變數位址,此變數是用來傳回要開啟的子機碼代碼。如果呼叫 RegOpenKeyEx 成功,返回值為 ERROR_SUCCESS,而 ERROR_SUCCESS 其實是 0,這一點和大部分的 API 不同;如果呼叫失敗,返回錯誤碼 ( 錯誤碼不為 0 )。有關登錄資料庫的 API,都是如此,如果成功,傳回 ERROR_SUCCESS;如果失敗,返回錯誤值。

RegQueryValueEx

要獲得或查詢某個子機碼中的某項數值資料及資料類型,就得呼叫 RegQueryValueEx,其原型為

LONG WINAPI RegQueryValueEx(
  __in          HKEY    hKey,
  __in_opt      LPCTSTR lpValueName,
  __reserved    LPDWORD lpReserved,
  __out_opt     LPDWORD lpType,
  __out_opt     LPBYTE  lpData,
  __inout_opt   LPDWORD lpcbData
);

第一個參數是 hKey,就是指要查詢的子機碼代碼,它必須是經由 KEY_QUERY_VALUE 呼叫 RegOpenKeyEx 所開啟的子機碼,也可以是 HKEY_CLASSES_ROOT、HKEY_CURRENT_USER、HKEY_LOCAL_MACHINE、HKEY_USERS、HKEY_PERFORMANCE_DATA、HKEY_CURRENT_CONFIG 等。第二個參數,lpValueName,是一字串的位址,此字串為第一個參數,hKey,中的某一個數值名稱,如果 lpValueName 為 0 或指向空字串,那麼 RegQueryValueEx 就會取得子機碼,hKey,預設值數值的資料及資料類型。如果 hKey 中沒有 lpValueName 所指字串的數值名稱,那麼會返回 ERROR_FILE_NOT_FOUND。

第三個參數,lpReserved,必須是 0,為系統所保留。第四個參數,lpType,是某個變數的位址,RegQueryValueEx 會把取得的數值之資料類型存入此變數堙C如果您覺得不須獲得資料類型,可以把 lpType 設為 0。第五個參數,lpData,也是某塊記憶體之位址,RegQueryValueEx 會把取得的數值之資料存入此記憶體堙A此記憶體長度由 lpcbData 決定。如果您覺得不須獲得資料,可以把 lpData 設為 0。

最後一個參數,lpcbData,是一個變數之位址,此變數用來設定 lpData 所指記憶體有多大,以位元組為單位。在呼叫 RegQueryValueEx 成功後,系統會把實際讀取到的資料長度存入此變數堙C如果 lpcbData 所指變數堛獐ぉ太小,而使該記憶體不足以容納所有資料,這時候 RegQueryValueEx 會返回 ERROR_MORE_DATA,並把應有的大小存入 lpcbData 所指變數堙A此刻 lpData 所指的記憶體內容為垃圾資料。如果所獲取的資料類型為 REG_SZ、REG_MULTI_SZ 或 REG_EXPAND_SZ,那麼 lpcbData 所指變數指的是包含 0 的數值資料長度。如果 lpData 為 0,那麼 lpcbData 就沒有意義了,所以也應該設為 0;但是此刻 lpcbData 也可以不設為 0,而是設為真正指向某個變數的位址,這時系統會把數值的資料長度傳到該變數堙A也會把資料形態存入 lpData 所指的變數堙A然後傳回 ERROR_SUCCESS。程式設計師可以利用此項特性,呼叫兩次 RegQueryValueEx,第一次讓 lpData 為 0,lpcbData 指向某個變數的位址,這樣就能得到數值資料長度,然後再配置適當大小的記憶體區塊;第二次才讓 lpData 真的指向此記憶體區塊,lpcbData 仍指向那個變數,這樣就可以不必瞎猜要配置多少記憶體,而能獲得數值的資料。

RegQueryValueEx 的返回值,可能有以下幾種情形:

  1. 若成功,返回 ERROR_SUCCESS。
  2. 若 lpData 所指的記憶體太小,系統傳回 ERROR_MORE_DATA,並且系統也會把適當的記憶體大小填入 lpcbData 所指的變數堙C
  3. 若 lpValueName 所指數值名稱字串不存在,那系統會傳回 ERROR_FILE_NOT_FOUND。
  4. 其他錯誤,請參考 MSDN 的「system error code」。

RegEnumKeyEx

如果想獲取某個機碼內,有哪些子機碼時,應進入一個迴圈,重複呼叫 RegEnumKeyEx,就能把這些子機碼一一列舉出來。RegEnumKeyEx 的原型是:

LONG WINAPI RegEnumKeyEx(
  __in         HKEY      hKey,
  __in         DWORD     dwIndex,
  __out        LPTSTR    lpName,
  __inout      LPDWORD   lpcName,
  __reserved   LPDWORD   lpReserved,
  __inout      LPTSTR    lpClass,
  __inout_opt  LPDWORD   lpcClass,
  __out_opt    PFILETIME lpftLastWriteTime
);

第一個參數,hKey,是機碼代碼。而程式想要獲取這個機碼下一層的所有子機碼資料,hKey 必須具有 KEY_ENUMERATE_SUB_KEYS 存取權的機碼代碼,可以經由 KEY_ENUMERATE_SUB_KEYS 為參數呼叫 RegCreateKeyEx、RegCreateKeyTransacted、RegOpenKeyEx、RegOpenKeyTransacted 等 API,這些 API 的傳回值就是具有 KEY_ENUMERATE_SUB_KEYS 存取權的機碼代碼。hKey 也可以是 HKEY_CLASSES_ROOT、HKEY_CURRENT_CONFIG、HKEY_CURRENT_USER、HKEY_LOCAL_MACHINE、HKEY_PERFORMANCE_DATA、HKEY_USERS 預設的機碼。第二個參數,dwIndex,是要獲得的子機碼編號,當程式第一次呼叫 RegEnumKeyEx 時,dwIndex 應設為 0,就能得到第零個子機碼的資料,然後進入迴圈,重複呼叫 RegEnumKeyEx,但每次呼叫時把 dwIndex 加一,就能獲得下一個子機碼的資料,如此每次使 dwIndex 加一,直到返回值不為 0 而是 ERROR_NO_MORE_ITEMS 為止,就能列舉出 hKey 內所有子機碼的資料。當然程式也能先呼叫 RegQueryInfoKey,取得有幾個子機碼,然後第一次呼叫 RegEnumKeyEx 時,把 dwIndex 設為最後一個子機碼編號,而進入迴圈時,每次使 dwIndex 減一直到變為 0 為止。

第三個參數,lpName 是一塊記憶體位址,用來存放子機碼的名稱。如果呼叫成功,系統會把子機碼名稱放入此記憶體區愧內,但不包含上一層的機碼名稱;如果呼叫失敗,則不變更此記憶體區塊內容。第四個參數,lpcName,是一個變數的位址,用來指定 lpName 的大小,以字元為單位。如果呼叫成功,系統會把子機碼名稱的長度存入這個變數堙C第五個參數,lpReserved,是保留參數,應設為 0。

第六個參數,lpClass,是一塊記憶體的位址,系統會把子機碼的類別存入此位址中。第七個參數,lpcClass,是一變數位址,系統會把子機碼類別長度存入此變數堙A以字元為單位。第八個變數,lpftLastWriteTime,是 FILETIME 結構體的位址,系統會把子機碼最後寫入的時間,存入此結構體內。

RegEnumValue

如果不知某個機碼內有哪些數值時,可以呼叫 RegEnumValue 把這些數值一一列舉出來。RegEnumValue 的原型是:

LONG WINAPI RegEnumValue(
  __in          HKEY    hKey,
  __in          DWORD   dwIndex,
  __out         LPTSTR  lpValueName,
  __inout       LPDWORD lpcchValueName,
  __reserved    LPDWORD lpReserved,
  __out_opt     LPDWORD lpType,
  __out_opt     LPBYTE  lpData,
  __inout_opt   LPDWORD lpcbData
);

第一個參數,hKey,就是要列舉出來的機碼代碼,它必須是經由 KEY_QUERY_VALUE 呼叫 RegOpenKeyEx、RegCreateKeyEx 所返回的子機碼,也可以是 HKEY_CLASSES_ROOT、HKEY_CURRENT_USER、HKEY_LOCAL_MACHINE、HKEY_USERS、HKEY_PERFORMANCE_DATA、HKEY_CURRENT_CONFIG 等。第二個參數,dwIndex,是要獲得的數值編號,當程式第一次呼叫時,dwIndex 為 0,下一次呼叫時把 dwIndex 加一,就能獲得下一個數值,如此每次使 dwIndex 增一,直到返回值不為 0 為止,就能列舉出所有 hKey 之內的數值。

第三個參數,lpValueName,為一記憶體區塊的位址,系統會把數值名稱存入此記憶體區塊內,這個名稱會以 0 為結尾,此記憶體區塊也必須夠大,能存入包含結尾的 0 及數值名稱。第四個參數,lpcchValueName,是一個變數的位址,這個變數用來表示數值名稱的長度,不包含結尾的 0,以字元為單位。第五個參數,lpReserved,為系統保留,須設為 0。

第六個參數,lpType,為一變數的位址,系統會把數值的資料類型存放在此變數堙C如果您不用到資料類型,可以把 lpType 設為 0,那麼系統就不會傳回資料類型。第七個參數,lpData,是一記憶體位址,系統傳回的資料就存放在這兒。如果您不用到資料,可以把 lpData 設為 0。最後一個參數,lpcbData,是一個變數的位址,系統會把資料長度存入此變數堙A資料長度的單位是位元組。

RegQueryInfoKey

RegQueryInfoKey 能獲得某個機碼內中含有幾個子機碼、幾個數值,並且也能獲取這些子機碼、數值中,名稱最長的有多少位元組。因此 RegQueryInfoKey 最重要的用途便是在呼叫 RegQueryValueEx、RegEnumValue 等 API 時,事先獲得要設定多少記憶體,以供存放所獲得的資料之用。RegQueryInfoKey 的原型是:

LONG WINAPI RegQueryInfoKey(
  __in         HKEY      hKey,
  __out        LPTSTR    lpClass,
  __inout_opt  LPDWORD   lpcClass,
  __reserved   LPDWORD   lpReserved,
  __out_opt    LPDWORD   lpcSubKeys,
  __out_opt    LPDWORD   lpcMaxSubKeyLen,
  __out_opt    LPDWORD   lpcMaxClassLen,
  __out_opt    LPDWORD   lpcValues,
  __out_opt    LPDWORD   lpcMaxValueNameLen,
  __out_opt    LPDWORD   lpcMaxValueLen,
  __out_opt    LPDWORD   lpcbSecurityDescriptor,
  __out_opt    PFILETIME lpftLastWriteTime
);

第一個參數,hKey,是指要獲取哪一個機碼內的資料,必須是以 KEY_QUERY_VALUE 存取權為參數,呼叫 RegCreateKeyEx、RegCreateKeyTransacted、RegOpenKeyEx、RegOpenKeyTransacted 等 API 的返回值,也可以是 HKEY_CLASSES_ROOT、HKEY_CURRENT_USER、HKEY_LOCAL_MACHINE、HKEY_USERS、HKEY_PERFORMANCE_DATA、HKEY_CURRENT_CONFIG 等預設值。第二個參數,lpClass,指向一記憶體位址,此記憶體用來獲得類別名稱,僅用於 NT 為核心的 Windows 系統,也可設為 0,表示不獲取類別名稱。第三個參數,lpcClass,為一變數位址,此變數用來傳回類別名稱的長度。第四個參數,lpReserved,為保留的參數,必須設為 0。

第五個參數,lpcSubKeys,為一變數位址,此變數用來傳回 hKey 中所含的子機碼個數。lpcSubKeys 可以為 0,表示不需要獲得子機碼個數。第六個參數,lpcMaxSubKeyLen,為一變數位址,此變數用來傳回 hKey 中所含的子機碼名稱最長的有多少個字元,而此傳回的最長子機碼名稱並不包含 NULL 字元,因此如以位元組為單位,最長名稱的記憶體大小應為 [lpcMaxSubKeyLen]×2+2。lpcMaxSubKeyLen 也可以設為 0,表示不需獲得最長的子機碼。第七個參數,lpcMaxClassLen,為一變數位址,用來傳回 hKey 中所含的類別名稱最長的有多少個字元,同樣不包含 NULL,如果不需要最長的類別名稱,lpcMaxClassLen 也可以設為 0。第八個參數,lpcValues,為一變數的位址,此變數用來傳回 hKey 中所含的數值個數,如果不需要也可以設為 0。

第九個參數,lpcMaxValueNameLen,為一變數位址,此變數用來傳回數值名稱最長的有多少個字元,同樣不包含 NULL,如果不需要最長的數值名稱,lpcMaxValueNameLen 也可以設為 0。第十個參數,lpcMaxValueLen,為一變數位址,此變數用來傳回這些數值的資料中,資料長度最大的有多少個位元組,如果不需要最長也可以設為 0。第十一個參數,lpcbSecurityDescriptor,為一變數位址,此變數用來傳回機碼的 security descriptor,僅用於 NT 系統,如不需要也可以設為 0。最後一個參數,lpftLastWriteTime,為一 FILETIME 結構體位址,這個 FILETIME 結構體記錄最後一次寫入此機碼的最後時間,僅用於 NT 系統,如不需要也可以設為 0。

如果呼叫 RegQueryInfoKey 成功,傳回 ERROR_SUCCESS。

RegCreateKeyEx

RegCreateKeyEx 會在登錄資料庫堳堨艉@個機碼,假如此機碼已經存在了,那麼 RegCreateKeyEx 就會開啟此機碼。不管此機碼存在與否,RegCreateKeyEx 都會開啟此機碼,因此如果後來用不著,都應該關閉。底下是 RegCreateKeyEx 的原型:

LONG WINAPI RegCreateKeyEx(
  __in        HKEY                  hKey,
  __in        LPCTSTR               lpSubKey,
  __reserved  DWORD                 Reserved,
  __in_opt    LPTSTR                lpClass,
  __in        DWORD                 dwOptions,
  __in        REGSAM                samDesired,
  __in_opt    LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  __out       PHKEY                 phkResult,
  __out_opt   LPDWORD               lpdwDisposition
);

第一個參數,hKey,是以 KEY_CREATE_SUB_KEY 為參數呼叫 RegCreateKeyEx 或 RegOpenKeyEx 所返回的機碼代碼,也可以是系統預設的機碼,例如 HKEY_CLASSES_ROOT、HKEY_CURRENT_CONFIG、HKEY_CURRENT_USER、HKEY_LOCAL_MACHINE、HKEY_USERS。第二個參數,lpSubKey,為一結尾為 0 的字串位址,而此字串最多可達 32 層子機碼的組合,字串的第一個子機碼會建立在 hKey 之下一層。例如,要在「HKEY_CLASSES_ROOT」底下建立一連串的子機碼,「asmfile\shell\edit\command」,可以使 hKey 為 HKEY_CLASSES_ROOT,lpSubKey 為指向「asmfile\shell\edit\command」字串的位址,這樣,即使原來的 HKEY_CLASSES_ROOT 沒有 asmfile 子機碼,也能建立起來包含從 asmfile 到 command 四個子機碼。當然,也可以一層一層建立。第三個參數,Reserved,為系統所保留,應設為 0。

第四個參數,lpClass,為一位址,該位址為類別名稱的字串位址,也可以設為 0,表示不使用留待以後設定。第五個參數,dwOptions,可以是下表中的某一項常數:

dwOptions數值說明
REG_OPTION_NON_VOLATILE0此為預設值,表示所建立的機碼會保存起來,即使程式結束、或系統重新開機都能存在登錄資料庫堙C
REG_OPTION_VOLATILE1表示所建立的機碼僅存於記憶體中,當重新載入登錄資料庫時,並不會保留此機碼,尤其是 HKEY_LOCAL_MACHINE 底下的機碼常發生此種情形。如果呼叫 RegCreateKeyEx 時,該機碼已經存在而使用者設定此旗標,則系統會忽略此旗標。
REG_OPTION_CREATE_LINK2建立 symbolic link 的機碼。
REG_OPTION_BACKUP_RESTORE4如果設定此旗標,那麼 RegCreateKeyEx 會忽略 samDesired 參數,並嘗試用所需的權限開啟或建立機碼,以備份或還原。

RegCloseKey

當程式開啟或建立某個機碼,經處理而不再使用時,必須關閉該機碼,這時候就要呼叫 RegCloseKey,其原型為:

LONG WINAPI RegCloseKey(
     __in   HKEY    hKey
);

這個 API 只有一個參數,就是要關閉的子機碼,hKey。


註解

註一:環境變數 ( Environment Variables )

%WinDir% 其實是代表「Windows」的所在目錄。使用者在安裝 Windows 時,可以更改 Windows 子目錄的名稱,也可以改裝在其他磁碟機上,甚至系統本身就不叫「WINDOWS」( 例如 Windows NT/2000 預設為 C:\WINNT ),故系統常以 %WinDir% 表示。這些變數稱為環境變數,主要的變數可參考下表,更詳細的資料,請參考「Dev::Coder 在電梯裡遇見雙胞胎」網頁的說明。

系統變數預設值說 明
%HomeDrive%C:啟動系統的所在磁碟機
%SystemRoot%C:\WINDOWS啟動的系統所在子目錄,是系統內建變數 ( built-in variable )
%WinDir%C:\WINDOWS啟動的系統所在子目錄,和 %SystemRoot% 幾乎相同,但為一般變數 ( regular variable ),且比 %SystemRoot% 早出現。
%UserProfile%Win XP:C:\Documents and Settings\pinocchio
Win 7:C:\Users\pinocchio
當前使用者,pinocchio,資料變數
%temp%Win XP:C:\Documents and Settings\pinocchio\Local Settings\Temp
Win 7:C:\Users\pinocchio\AppData\Local\Temp
當前使用者,pinocchio,的暫存區
%ProgramData% C:\ProgramData ( 隱藏目錄 ) 應用程式存放資料的地方,這是 Vista 之後才有的目錄,XP 對應的位置是在 %ALLUSERPROFILE%。因為 %ProgramFiles% 對權限控管較嚴格,為了防止程式被惡意竄改,就算是應用程式本身也不能對安裝目錄做寫入,也因此才會將應用程式的資料拆開來存放。
%ProgramFiles%C:\Program Files 應用程式安裝的位置。
%ProgramFiles(x86)%C:\Program Files (x86) 這個環境變數只會出現在 64 位元的系統,做為安裝 32 位元應用程式的位置。

親愛的讀者,您也可以開啟「命令提示字元」,在裡面輸入「echo 環境變數」,再按下「Enter」鍵,也可以得到該環境變數之值。如下圖:

註二:64 位元的 Windows

64 位元的 Windows 系統,仍把許多重要的動態連結程式庫 ( DLL ) 放在「%SystemRoot%\SYSTEM32」子目錄堙A當 64 位元的程式呼叫 KERNEL32.DLL、USER32.DLL 等動態連結程式庫堛 API 時,仍然是到「%SystemRoot%\SYSTEM32」子目錄堨h尋找、載入。這麼做因為回溯相容的緣故,因為許多程式採用寫死使用該路徑的方式,不得已,只好掛羊頭、賣狗肉。

註三:在「.txt」或「.副檔名」堶悼i能會有的數值

PerceivedType

在「.txt」機碼堙A或者說在「.副檔名」的堶情A還可能會有名為「PerceivedType」的數值,它的資料類型通常是 REG_SZ,說明這類副檔名是屬於哪一大類型的檔案,可能的數值如下表

PerceivedType說明
Folder資料夾
Text純文字檔,如 txt、html、c、cpp、def 等等
Image圖片檔,如 bmp、jpg、png、tiff、ico 等等
Audio聲音檔,如 voc、mp3、mid、wma 等等
Video影像檔,如 mkv、mpg、avi、wmv 等等
Compressed壓縮檔,如 rar、zip、tar、 等等
Document文件檔,如 doc、docx、xlsx、pptx 等等
System系統檔,如 local、chk、vxd、386、hxd 等等
Application
Gamemedia
Contacts

Content Type

這個數值是告訴瀏覽器如何處理從網路接收來的檔案。例如「.gif」機碼中有「Content Type」數值,而且其資料為「image/gif」,當瀏覽器 ( 如 Internet Explorer、Firefox、Google Chrome ) 接收到從網際網路傳來的 gif 檔案時,瀏覽器就不會詢問你是否儲存,而是把它顯示在螢幕上。至於「Content Type」的資料有哪些,可以查大陸「OSC 的在線工具」網頁。