將繁瑣的 Win32 登錄表 API (RegOpenKeyEx, RegQueryValueEx, RegSetValueEx 等) 封裝為簡潔、型別安全、錯誤處理完善的 C++ 類別,支援字串 (REG_SZ)、DWORD (REG_DWORD)、二進制 (REG_BINARY) 三種常用資料型別。
class CRegistryUtil {
public:
// 讀取值 (自動偵測型別或指定型別)
bool ReadString(HKEY hRoot, LPCTSTR subKey, LPCTSTR valueName, CString& outStr);
bool ReadDword(HKEY hRoot, LPCTSTR subKey, LPCTSTR valueName, DWORD& outDword);
bool ReadBinary(HKEY hRoot, LPCTSTR subKey, LPCTSTR valueName, BYTE* pBuffer, DWORD& bufSize);
// 寫入值
bool WriteString(HKEY hRoot, LPCTSTR subKey, LPCTSTR valueName, LPCTSTR str);
bool WriteDword(HKEY hRoot, LPCTSTR subKey, LPCTSTR valueName, DWORD dword);
bool WriteBinary(HKEY hRoot, LPCTSTR subKey, LPCTSTR valueName, const BYTE* data, DWORD len);
// 鍵值管理
bool DeleteValue(HKEY hRoot, LPCTSTR subKey, LPCTSTR valueName);
bool DeleteKey(HKEY hRoot, LPCTSTR subKey, LPCTSTR keyName); // 刪除子鍵 (WinNT 需遞迴)
bool CreateKey(HKEY hRoot, LPCTSTR subKey); // 確保鍵存在
// 列舉
bool EnumValues(HKEY hRoot, LPCTSTR subKey, std::vector<CString>& names);
bool EnumKeys(HKEY hRoot, LPCTSTR subKey, std::vector<CString>& names);
};
HKEY OpenKey(HKEY hRoot, LPCTSTR subKey, REGSAM access = KEY_READ) {
HKEY hKey = NULL;
LONG res = RegOpenKeyEx(hRoot, subKey, 0, access, &hKey);
return (res == ERROR_SUCCESS) ? hKey : NULL;
}
void SafeCloseKey(HKEY& hKey) {
if (hKey) { RegCloseKey(hKey); hKey = NULL; }
}
bool CRegistryUtil::ReadString(HKEY hRoot, LPCTSTR subKey, LPCTSTR valueName, CString& outStr) {
HKEY hKey = OpenKey(hRoot, subKey, KEY_QUERY_VALUE);
if (!hKey) return false;
DWORD type = 0, size = 0;
// 第一次查詢取得大小
LONG res = RegQueryValueEx(hKey, valueName, NULL, &type, NULL, &size);
if (res != ERROR_SUCCESS || type != REG_SZ) { SafeCloseKey(hKey); return false; }
// 分配緩衝區並讀取
std::vector<TCHAR> buf(size / sizeof(TCHAR));
res = RegQueryValueEx(hKey, valueName, NULL, &type, (LPBYTE)buf.data(), &size);
SafeCloseKey(hKey);
if (res == ERROR_SUCCESS) {
outStr = buf.data();
return true;
}
return false;
}
bool CRegistryUtil::WriteDword(HKEY hRoot, LPCTSTR subKey, LPCTSTR valueName, DWORD dword) {
HKEY hKey = OpenKey(hRoot, subKey, KEY_SET_VALUE);
if (!hKey) return false;
// 若鍵不存在,嘗試建立
if (!hKey) {
DWORD disp;
RegCreateKeyEx(hRoot, subKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hKey, &disp);
if (!hKey) return false;
}
LONG res = RegSetValueEx(hKey, valueName, 0, REG_DWORD, (const BYTE*)&dword, sizeof(DWORD));
SafeCloseKey(hKey);
return res == ERROR_SUCCESS;
}
bool CRegistryUtil::DeleteKey(HKEY hRoot, LPCTSTR subKey, LPCTSTR keyName) {
HKEY hParent = OpenKey(hRoot, subKey, KEY_ALL_ACCESS);
if (!hParent) return false;
// Windows NT/2000/XP: RegDeleteKey 只能刪空鍵,需遞迴刪除子鍵
// Windows 9x: RegDeleteKey 會遞迴刪除
// 統一實現遞迴刪除
bool success = RecursiveDeleteKey(hParent, keyName);
SafeCloseKey(hParent);
return success;
}
bool CRegistryUtil::RecursiveDeleteKey(HKEY hParent, LPCTSTR keyName) {
HKEY hKey;
if (RegOpenKeyEx(hParent, keyName, 0, KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS)
return false;
// 列舉並遞迴刪除子鍵
TCHAR subKeyName[256];
DWORD index = 0, size;
while (RegEnumKeyEx(hKey, index, subKeyName, &size, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
if (!RecursiveDeleteKey(hKey, subKeyName)) {
RegCloseKey(hKey);
return false;
}
// 註意:刪除後索引不變,因為下一個子鍵會移到當前索引
}
RegCloseKey(hKey);
// 刪除自身
return RegDeleteKey(hParent, keyName) == ERROR_SUCCESS;
}
CRegistryUtil reg;
// 寫入各型別值
reg.WriteString(HKEY_CURRENT_USER, _T("Software\\MyApp"), _T("UserName"), _T("JohnDoe"));
reg.WriteDword(HKEY_CURRENT_USER, _T("Software\\MyApp"), _T("MaxConnections"), 100);
BYTE binData[] = {0x01, 0x02, 0x03, 0x04};
reg.WriteBinary(HKEY_CURRENT_USER, _T("Software\\MyApp"), _T("LicenseKey"), binData, 4);
// 讀取驗證
CString name; DWORD maxConn; BYTE buf[16]; DWORD bufSize = 16;
reg.ReadString(HKEY_CURRENT_USER, _T("Software\\MyApp"), _T("UserName"), name);
reg.ReadDword(HKEY_CURRENT_USER, _T("Software\\MyApp"), _T("MaxConnections"), maxConn);
reg.ReadBinary(HKEY_CURRENT_USER, _T("Software\\MyApp"), _T("LicenseKey"), buf, bufSize);
// 清理
reg.DeleteValue(HKEY_CURRENT_USER, _T("Software\\MyApp"), _T("UserName"));
reg.DeleteKey(HKEY_CURRENT_USER, _T("Software\\MyApp"), _T("Settings")); // 刪除整個子鍵
| 問題 | 原因 | 解決 |
|---|---|---|
| 存取被拒 | 權限不足 (寫入 HKLM 需管理員) | 檢查 UAC、以管理員身分執行、或改寫 HKCU |
| 資料截斷 | 緩衝區太小 | 先查詢大小再分配緩衝區 (如 ReadString 實現) |
| 型別不符 | REG_SZ vs REG_EXPAND_SZ | 讀取時檢查 type,必要時用 ExpandEnvironmentStrings |
| 刪除失敗 | 鍵非空 (NT 系統) | 使用遞迴刪除函數 |
REG_MULTI_SZ (多字串) 支援CRegKey 包裝類別KEY_WOW64_64KEY)RegCreateKeyTransacted)