Windows 登錄表採樹狀結構儲存系統配置、硬體資訊、應用程式設定與使用者偏好。VC++ 透過 Win32 API (advapi32.lib) 提供完整的讀寫介面。本文以「查詢/修改註冊使用者與公司名稱」為例,示範標準錯誤處理流程與資料型別轉換技巧。
| 根鍵 (HKEY) | 用途 |
|---|---|
HKEY_LOCAL_MACHINE (HKLM) | 電腦層級設定 (硬體、軟體、安全性) |
HKEY_CURRENT_USER (HKCU) | 當前使用者設定 (桌面、應用程式偏好) |
HKEY_CLASSES_ROOT (HKCR) | 檔案關聯、COM 類別註冊 |
HKEY_USERS (HKU) | 所有使用者配置檔 |
HKEY_CURRENT_CONFIG (HKCC) | 當前硬體設定檔 |
目標鍵值位於:HKLM\Software\Microsoft\Windows\CurrentVersion
RegisteredOwner (REG_SZ) — 註冊使用者名稱RegisteredOrganization (REG_SZ) — 註冊公司名稱RegOpenKeyEx — 開啟鍵,取得控制代碼 (HKEY)RegQueryValueEx / RegSetValueEx — 讀取/寫入值RegCloseKey — 關閉控制代碼 (必要!)void CRegDemoDlg::OnQueryUserInfo()
{
HKEY hKey = NULL;
LONG ret;
LPCTSTR subKey = _T("Software\\Microsoft\\Windows\\CurrentVersion");
// 1. 開啟鍵 (唯讀)
ret = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, subKey, 0, KEY_READ, &hKey);
if (ret != ERROR_SUCCESS)
{
AfxMessageBox(_T("無法開啟登錄表鍵值!錯誤碼: ") + std::to_wstring(ret));
return;
}
// 2. 查詢 RegisteredOwner
TCHAR szOwner[256] = {0};
DWORD cbData = sizeof(szOwner);
DWORD dwType = 0;
ret = ::RegQueryValueEx(hKey, _T("RegisteredOwner"), NULL, &dwType, (LPBYTE)szOwner, &cbData);
if (ret != ERROR_SUCCESS || dwType != REG_SZ)
{
::RegCloseKey(hKey);
AfxMessageBox(_T("讀取使用者名稱失敗!"));
return;
}
// 3. 查詢 RegisteredOrganization
TCHAR szOrg[256] = {0};
cbData = sizeof(szOrg);
ret = ::RegQueryValueEx(hKey, _T("RegisteredOrganization"), NULL, &dwType, (LPBYTE)szOrg, &cbData);
::RegCloseKey(hKey); // 記得關閉!
if (ret != ERROR_SUCCESS || dwType != REG_SZ)
{
AfxMessageBox(_T("讀取公司名稱失敗!"));
return;
}
// 4. 顯示結果
CString strMsg;
strMsg.Format(_T("註冊使用者: %s\r\n註冊公司: %s"), szOwner, szOrg);
AfxMessageBox(strMsg);
}
void CRegDemoDlg::OnModifyUserInfo()
{
// 1. 取得使用者輸入 (假設從編輯框 m_editOwner, m_editOrg)
CString strOwner, strOrg;
m_editOwner.GetWindowText(strOwner);
m_editOrg.GetWindowText(strOrg);
if (strOwner.IsEmpty() || strOrg.IsEmpty())
{
AfxMessageBox(_T("請輸入完整資訊!"));
return;
}
// 2. 字串轉位元組陣列 (含結束符)
auto ToByteArray = [](const CString& str) -> std::vector
{
int len = str.GetLength();
std::vector bytes((len + 1) * sizeof(TCHAR));
memcpy(bytes.data(), str.GetString(), (len + 1) * sizeof(TCHAR));
return bytes;
};
std::vector ownerBytes = ToByteArray(strOwner);
std::vector orgBytes = ToByteArray(strOrg);
HKEY hKey = NULL;
LONG ret;
LPCTSTR subKey = _T("Software\\Microsoft\\Windows\\CurrentVersion");
// 3. 開啟鍵 (寫入權限)
ret = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, subKey, 0, KEY_WRITE, &hKey);
if (ret != ERROR_SUCCESS)
{
AfxMessageBox(_T("無法開啟鍵值進行寫入!需管理員權限。錯誤碼: ") + std::to_wstring(ret));
return;
}
// 4. 寫入 RegisteredOwner
ret = ::RegSetValueEx(hKey, _T("RegisteredOwner"), 0, REG_SZ, ownerBytes.data(), (DWORD)ownerBytes.size());
if (ret != ERROR_SUCCESS)
{
::RegCloseKey(hKey);
AfxMessageBox(_T("寫入使用者名稱失敗!錯誤碼: ") + std::to_wstring(ret));
return;
}
// 5. 寫入 RegisteredOrganization
ret = ::RegSetValueEx(hKey, _T("RegisteredOrganization"), 0, REG_SZ, orgBytes.data(), (DWORD)orgBytes.size());
::RegCloseKey(hKey);
if (ret != ERROR_SUCCESS)
{
AfxMessageBox(_T("寫入公司名稱失敗!錯誤碼: ") + std::to_wstring(ret));
return;
}
AfxMessageBox(_T("修改成功!請重新啟動電腦或重新登入生效。"));
}
| 項目 | 查詢 | 修改 |
|---|---|---|
| 開啟權限 | KEY_READ | KEY_WRITE (需管理員) |
| 資料型別 | REG_SZ (以 null 結尾字串) | REG_SZ |
| 緩衝區大小 | 傳入緩衝區大小,函數回傳實際大小 | 傳入實際資料大小 (含 null 結束符) |
| 字串轉換 | 直接接收至 TCHAR[] | CString → std::vector 確保編碼一致 |
| 錯誤碼 | ERROR_SUCCESS (0) 表示成功 | 同左 |
寫入 HKLM 需管理員權限。解決方案:
requireAdministratorHKCU\Software\Microsoft\Windows\CurrentVersion (使用者層級,無需提權)專案設定為 Unicode (預設) 時,TCHAR=WCHAR,RegSetValueEx 需傳入 UTF-16 位元組陣列。若目標系統預期 ANSI,需手動 WideCharToMultiByte 轉換並指定 REG_SZ (系統會自動處理 Unicode↔ANSI 轉換)。
RegQueryValueEx 的 lpcbData 為 位元組數,非字元數。Unicode 下 sizeof(TCHAR)=2,配置緩衝區時務必 (maxChars + 1) * sizeof(TCHAR)。
使用 RegConnectRegistry 連接遠端電腦 \\RemotePC\HKEY_LOCAL_MACHINE,需遠端登錄服務啟動且具權限。
// 現代化寫法:RAII 自動關閉、例外安全
#include // CRegKey 定義於 ATL
void ModernRegWrite()
{
CRegKey key;
LONG ret = key.Open(HKEY_CURRENT_USER, _T("Software\\MyApp"), KEY_WRITE);
if (ret != ERROR_SUCCESS)
ret = key.Create(HKEY_CURRENT_USER, _T("Software\\MyApp")); // 不存在則建立
if (ret == ERROR_SUCCESS)
{
key.SetStringValue(_T("UserName"), _T("JohnDoe"));
key.SetDWORDValue(_T("Version"), 2024);
// 解構子自動 Close()
}
}
登錄表操作看似簡單,實則暗藏權限、編碼、緩衝區、遠端等細節地雷。掌握 RegOpenKeyEx/Query/SetValueEx/CloseKey 標準三部曲,搭配 CRegKey RAII 封裝與完善錯誤碼檢查,即能編寫健壯、可維護的設定存取程式碼。現代應用建議優先考慮 HKCU 或應用程式專屬資料夾 (SHGetKnownFolderPath(FOLDERID_LocalAppData)),減少對系統級登錄表的依賴。