首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC++

VC++ 登錄表操作完整指南:從 RegOpenKeyEx 到 RegSetValueEx 的實戰範例

     編輯:VC++

前言:登錄表是 Windows 設定的中樞神經

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) — 註冊公司名稱

核心 API 流程

  1. RegOpenKeyEx — 開啟鍵,取得控制代碼 (HKEY)
  2. RegQueryValueEx / RegSetValueEx — 讀取/寫入值
  3. 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_READKEY_WRITE (需管理員)
資料型別REG_SZ (以 null 結尾字串)REG_SZ
緩衝區大小傳入緩衝區大小,函數回傳實際大小傳入實際資料大小 (含 null 結束符)
字串轉換直接接收至 TCHAR[]CString → std::vector 確保編碼一致
錯誤碼ERROR_SUCCESS (0) 表示成功同左

常見陷阱與最佳實務

⚠ 1. 權限提升 (UAC)

寫入 HKLM 需管理員權限。解決方案:

  • 應用程式清單設定 requireAdministrator
  • 或改寫 HKCU\Software\Microsoft\Windows\CurrentVersion (使用者層級,無需提權)

⚠ 2. 字元集一致性

專案設定為 Unicode (預設) 時,TCHAR=WCHAR,RegSetValueEx 需傳入 UTF-16 位元組陣列。若目標系統預期 ANSI,需手動 WideCharToMultiByte 轉換並指定 REG_SZ (系統會自動處理 Unicode↔ANSI 轉換)。

⚠ 3. 緩衝區大小計算

RegQueryValueEx 的 lpcbData 為 位元組數,非字元數。Unicode 下 sizeof(TCHAR)=2,配置緩衝區時務必 (maxChars + 1) * sizeof(TCHAR)。

⚠ 4. 遠端登錄表

使用 RegConnectRegistry 連接遠端電腦 \\RemotePC\HKEY_LOCAL_MACHINE,需遠端登錄服務啟動且具權限。

MFC 封裝類:CRegKey (ATL/MFC 共用)

// 現代化寫法: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)),減少對系統級登錄表的依賴。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved