前言:登錄表是 Windows 系統與應用程式的中樞設定存儲
.NET 在 Microsoft.Win32 命名空間提供 Registry(根鍵存取)與 RegistryKey(鍵值操作)兩個密封類別,封裝 Win32 Registry API(RegOpenKeyEx、RegSetValueEx、RegQueryValueEx 等)。本文系統整理根鍵對應、CRUD 操作、資料型別對應、權限與 UAC、遠端登錄、RegistryView(32/64 位元視圖)、異常處理與最佳實務。
一、七大根鍵與 Registry 靜態屬性
| Registry 屬性 | Win32 常數 | 用途 | 備註 |
Registry.ClassesRoot | HKEY_CLASSES_ROOT | 檔案關聯、COM 類別註冊、Shell 擴充 | HKLM\Software\Classes + HKCU\Software\Classes 合併視圖 |
Registry.CurrentUser | HKEY_CURRENT_USER | 當前使用者設定(應用程式偏好、環境變數) | HKU\ 別名,無需管理員權限 |
Registry.LocalMachine | HKEY_LOCAL_MACHINE | 機器層級設定(驅動程式、服務、系統原則) | 寫入需管理員權限(UAC 提升) |
Registry.Users | HKEY_USERS | 所有使用者配置檔 | 需管理員權限枚舉 |
Registry.CurrentConfig | HKEY_CURRENT_CONFIG | 當前硬體設定檔 | HKLM\System\CurrentControlSet\Hardware Profiles\Current 別名 |
Registry.DynData | HKEY_DYN_DATA | 動態硬體資訊(Win9x/Me,已棄用) | .NET 保留相容性,現代系統無效 |
Registry.PerformanceData | HKEY_PERFORMANCE_DATA | 效能計數器原始資料 | 唯讀、需特殊 API 解析 |
二、RegistryKey 核心 CRUD 方法
2.1 開啟/建立子鍵
// 唯讀開啟(預設)
RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\MyApp");
// 讀寫開啟
RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\MyApp", writable: true);
// 建立或開啟(自動建立不存在的路徑)
RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\MyApp");
// 建立指定存取控制(.NET 4.6+)
var security = new RegistrySecurity();
security.AddAccessRule(new RegistryAccessRule("Users", RegistryRights.WriteKey, AccessControlType.Allow));
RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\MyApp", true, RegistryOptions.None, security);
2.2 讀取值
// 回傳 object,需自行轉型
object val = key.GetValue("InstallPath"); // 不存在回傳 null
object val = key.GetValue("Version", "1.0.0"); // 不存在回傳預設值
int version = (int)key.GetValue("Version", 1); // 自動拆箱(需型別匹配)
string path = key.GetValue("Path") as string; // 安全轉型
// 取得所有值名稱
string[] names = key.GetValueNames();
// 取得值型別(RegistryValueKind)
RegistryValueKind kind = key.GetValueKind("Version"); // String, DWord, QWord, Binary, MultiString, ExpandString, None
2.3 寫入值
key.SetValue("InstallPath", @"C:\Program Files\MyApp"); // 預設 String
key.SetValue("Version", 2, RegistryValueKind.DWord); // 明確指定 DWORD (32-bit)
key.SetValue("LargeNumber", 1234567890123L, RegistryValueKind.QWord); // QWORD (64-bit)
key.SetValue("BinaryData", new byte[] { 0x01, 0x02, 0x03 }, RegistryValueKind.Binary);
key.SetValue("MultiString", new string[] { "A", "B", "C" }, RegistryValueKind.MultiString);
key.SetValue("ExpandString", @"%ProgramFiles%\MyApp", RegistryValueKind.ExpandString); // 含環境變數
2.4 刪除值與子鍵
key.DeleteValue("ObsoleteValue"); // 刪除單一值
key.DeleteValue("Missing", false); // false = 不存在不拋例外
key.DeleteSubKey("OldSubKey"); // 僅刪除空子鍵,非空拋例外
key.DeleteSubKeyTree("SubKeyWithChildren"); // 遞歸刪除子鍵及所有後代
2.5 關閉與釋放
key.Close(); // 或 key.Dispose(),釋放非受控 HKEY 控制代碼
// 建議使用 using 區塊
using (var key = Registry.CurrentUser.CreateSubKey(@"Software\MyApp")) {
key.SetValue("Test", 123);
} // 自動 Dispose
三、資料型別對應表
| .NET 型別 | RegistryValueKind | Win32 型別 | 範例 |
string | String (REG_SZ) | 以 null 結尾的 Unicode 字串 | "C:\Path" |
int / uint | DWord (REG_DWORD) | 32-bit 整數 | 1024 |
long / ulong | QWord (REG_QWORD) | 64-bit 整數 | 10000000000 |
byte[] | Binary (REG_BINARY) | 原始位元組陣列 | new byte[]{1,2,3} |
string[] | MultiString (REG_MULTI_SZ) | 多字串(雙 null 結尾) | new[]{"A","B"} |
string | ExpandString (REG_EXPAND_SZ) | 含環境變數參考 | "%TEMP%\file.log" |
四、權限模型、UAC 與 RegistryView
4.1 寫入 HKLM 需管理員權限
- 應用程式資訊清單(
app.manifest)設定 <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />。
- 或程式碼檢測:
new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator)。
- 非管理員寫入
HKLM → UnauthorizedAccessException。
4.2 32/64 位元登錄表視圖重導向
- 64 位元進程預設存取 64 位元視圖;32 位元進程(WoW64)預設重導向至
HKLM\Software\WOW6432Node。
- 跨視圖存取(.NET 4+):
RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64) / RegistryView.Registry32。
// 64 位元應用讀取 32 位元視圖
using (var key = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32)
.OpenSubKey(@"Software\Wow6432Node\MyApp")) { ... }
// 32 位元應用讀取 64 位元視圖
using (var key = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64)
.OpenSubKey(@"Software\MyApp")) { ... }
五、遠端登錄表操作
// 需遠端機器開啟「遠端登錄」服務、防火牆允許 RPC (TCP 135/445)、具備權限
using (var remoteKey = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, "SERVER01")) {
using (var subKey = remoteKey.OpenSubKey(@"Software\MyApp", writable: false)) {
var version = subKey?.GetValue("Version");
}
}
- 僅支援
RegistryHive.LocalMachine、Users、ClassesRoot、CurrentConfig。
CurrentUser 無遠端對應(屬於互動登入會話)。
六、實戰範例:應用程式安裝資訊登錄與讀取
public static class AppRegistry
{
private const string AppPath = @"Software\MyCompany\MyApp";
public static void RegisterInstall(string installPath, string version)
{
using (var key = Registry.CurrentUser.CreateSubKey(AppPath, true)) {
key.SetValue("InstallPath", installPath);
key.SetValue("Version", version);
key.SetValue("InstallDate", DateTime.UtcNow.ToString("o"));
key.SetValue("Features", new[] { "FeatureA", "FeatureB" }, RegistryValueKind.MultiString);
}
}
public static AppInfo GetInstallInfo()
{
using (var key = Registry.CurrentUser.OpenSubKey(AppPath, false)) {
if (key == null) return null;
return new AppInfo {
InstallPath = key.GetValue("InstallPath") as string,
Version = key.GetValue("Version") as string,
InstallDate = DateTime.TryParse(key.GetValue("InstallDate") as string, out var dt) ? dt : DateTime.MinValue,
Features = key.GetValue("Features") as string[] ?? Array.Empty()
};
}
}
public static void Unregister()
{
Registry.CurrentUser.DeleteSubKeyTree(AppPath, false);
}
}
public class AppInfo
{
public string InstallPath { get; set; }
public string Version { get; set; }
public DateTime InstallDate { get; set; }
public string[] Features { get; set; }
}
七、常見陷阱與最佳實務
- 永遠使用
using / try/finally 關閉 RegistryKey,避免控制代碼洩漏。
- 讀取前檢查
null:OpenSubKey 不存在回傳 null,GetValue 不存在回傳 null。
- 明確指定
RegistryValueKind 寫入數值/二進位/多字串,避免預設 String 導致讀取型別不符。
- 64 位元系統跨架構部署:安裝程式若為 32 位元,寫入
HKLM 會進入 Wow6432Node;64 位元讀取時需指定 RegistryView.Registry32。
- 敏感資料加密:登錄表非加密存儲,機密資訊(連線字串、Token)務必使用
ProtectedData (DPAPI) 加密後再寫入。
- 避免頻繁讀寫:登錄表非高效能資料庫,啟動時快取至記憶體,運行期讀取快取。
- 單元測試模擬:介面
IRegistry + MockRegistry 實作,避免測試污染真實登錄表。
八、現代替代方案:應用程式設定檔
| 方案 | 優點 | 缺點 | 適用場景 |
appsettings.json + IConfiguration | 結構化、型別安全、多環境、熱重載、跨平台 | 需檔案系統存取、部署需管理檔案 | .NET Core / .NET 5+ 應用程式設定 |
User Secrets (開發環境) | 敏感資料不進原始碼、專案隔離 | 僅開發環境 | 開發期連線字串、API Key |
Azure App Configuration / Consul | 集中管理、動態更新、審計、多租戶 | 需基礎設施、網路依賴 | 微服務、雲原生 |
| 登錄表 | 系統整合、COM/Shell 擴充、舊版相容、機器層級原則 | Windows 專用、權限複雜、非結構化、難以版控 | 安裝資訊、檔案關聯、系統原則、舊版相容 |
結語
Microsoft.Win32.Registry/RegistryKey 提供完整的登錄表程式化存取能力。掌握根鍵對應、CRUD 操作、資料型別對應、32/64 位元視圖重導向、遠端操作、權限與 UAC、安全性最佳實務,即能在 Windows 平台上安全、可靠地管理應用程式設定。現代 .NET 應用優先採用 appsettings.json + IConfiguration,登錄表保留給系統整合、安裝資訊、檔案關聯等 Windows 原生場景。