RegQueryValueEx每次都返回不同的值

RegQueryValueEx returning a different value every time

本文关键字:返回 RegQueryValueEx      更新时间:2023-10-16

我正在尝试读取注册表项值并显示它。我为自己创建了当前要读取的注册表项,并使用regedit:将其值设置为"foo">

HKEY hkey;
DWORD dwret = RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\Classes\foo", NULL, KEY_QUERY_VALUE, &hkey);
if (dwret == ERROR_SUCCESS)
{
TCHAR value[1024] = _T("");
DWORD dwvalue = 1024;
DWORD type = REG_SZ;
RegQueryValueEx(HKEY_CURRENT_USER, L"Software\Classes\test", NULL,&type , (LPBYTE)&value, &dwvalue);
cout << value;
RegCloseKey(hkey);
hkey = NULL;
}

问题是,如果我多次运行代码,我总是会得到不同的输出,下面是摘录:

00CFF5D0
001CF2D8
005EF5B8
0053F5A4

我误解什么了吗?RegQueryValueEx不是应该将键值存储到我的value中吗?如果是,为什么每次都得到不同的输出?

您的代码显然是为Unicode编译的,因此TCHAR映射到wchar_tRegQueryValueEx()映射到RegQueryValueExW()

但是std::cout没有用于wchar_t*指针的operator<<作为输入,但是它具有用于void*指针的operator<<,其打印出指针中包含的存储器地址。

ANY指针可以隐式转换为void*,所以这就是您实际调用的overator<<重载,这就是为什么您在输出中看到十六进制数字的原因。

每次运行代码时都会得到不同的值,因为value[]缓冲区每次都位于不同的内存地址。

为此,您需要:

  • 在定义UNICODE时使用std::wcout而不是std::cout

    #ifdef UNICODE
    wcout << value;
    #else
    cout << value;
    #endif
    

    或者:

    #ifdef UNICODE
    wostream &tcout = wcout;
    #else
    ostream &tcout = cout;
    #endif
    tcout << value;
    
  • value缓冲区从wchar_t转换为char,例如使用WideCharToMultiByte()或等效缓冲区,然后使用std::cout:输出

    #ifdef UNICODE
    char temp[1024];
    int len = WideCharToMultiByte(CP_ACP, 0, value, -1, temp, sizeof(temp)-1, NULL, NULL);
    temp[len] = 0;
    cout << temp;
    #else
    cout << value;
    #endif
    
  • RegQueryValueExA()char[]缓冲器一起使用,而不是将RegQueryValueEx()TCHAR[]缓冲器一起使用。让操作系统为您处理到char[]的转换:

    char value[1024];
    ...
    RegQueryValueExA(..., (LPBYTE)&value, ...);
    ...
    cout << value;
    

也就是说,您的代码还有其他几个问题。

  1. 您使用的是基于TCHAR的API,但使用的是硬编码的字符串文字。只有当您将项目设置为使用Unicode进行编译(您应该使用Unicode(时,这才会起作用。但是,为了在政治上正确,在TCHARAPI中使用字符串文字时,应该使用TEXT()宏。

    TEXT("Software\Classes\foo")
    TEXT("test")
    ...
    

    但是,您真的不应该再使用TCHARAPI了。它们早在早期就被引入,当时Windows还是一个基于ANSI的操作系统,正在迁移到Unicode。TCHAR帮助开发人员同时支持ANSI和Unicode版本的操作系统。但是那些日子已经一去不复返了。只使用Unicode API,除非您绝对需要ANSI API。忘记TCHAR的存在吧。

  2. 你打给RegQueryValueEx()的电话全错了。

    • 在使用value缓冲区之前,您没有进行任何错误检查以确保RegQueryValueEx()确实成功。

      dwret = RegQueryValueEx(...);
      if (dwret == ERROR_SUCCESS)
      ...
      else
      ...
      
    • 不能在其lpValueName参数中传递密钥路径,只能传递值名称。假设您的foo密钥具有test值,则需要传入打开的hkey,并仅指定"test"作为值名称:

      DWORD dwret = RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Software\Classes\foo"), NULL, KEY_QUERY_VALUE, &hkey);
      if (dwret == ERROR_SUCCESS)
      {
      ...
      dwret = RegQueryValueEx(hkey, TEXT("test"), ...);
      ...
      }
      
    • 您为lpcbData参数传入了错误的大小值。该参数以字节表示,但传入的大小以字符表示。由于您的代码是为Unicode编译的,其中sizeof(TCHAR)是2,这意味着RegQueryValueEx()只能写入一半的缓冲区。使用sizeof()获取完整字节大小:

      DWORD dwvalue = sizeof(value);
      
    • 即使RegQueryValueEx()成功,您也没有考虑到返回的字符串可能不会以null终止。对于使用小型手动字符串的简单测试,它可能会以null终止,但在生产代码中,您需要处理null终止符可能不总是存在的可能性。否则,请改用RegGetValue(),这样可以确保输出中始终存在空终止符,即使它不存在于源数据中。

  3. 无论RegQueryValueEx()成功还是失败,您都会将value缓冲区传递给std::cout。如果RegQueryValueEx()失败,缓冲区的内容将是不确定的,并且不能保证是null终止的,所以根本不要使用缓冲区。

试试类似的东西:

HKEY hkey = NULL;
LONG lret = RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\Classes\foo", NULL, KEY_QUERY_VALUE, &hkey);
if (lret == ERROR_SUCCESS)
{
WCHAR value[1024] = {};
DWORD dwvalue = sizeof(value);
DWORD type = REG_SZ;
lret = RegQueryValueExW(hkey, L"test", NULL, &type, (LPBYTE)&value, &dwvalue);
if (lret == ERROR_SUCCESS)
{
int len = (dwvalue / sizeof(WCHAR));
if ((len > 0) && (value[len-1] == 0))
--len;
wcout.write(value, len);
/* alternatively:
char temp[1024];
len = WideCharToMultiByte(CP_ACP, 0, value, len, temp, sizeof(temp), NULL, NULL);
cout.write(temp, len);
*/
}
RegCloseKey(hkey);
hkey = NULL;
}

或者:

HKEY hkey = NULL;
LONG lret = RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\Classes\foo", NULL, KEY_QUERY_VALUE, &hkey);
if (lret == ERROR_SUCCESS)
{
CHAR value[1024] = {};
DWORD dwvalue = sizeof(value);
DWORD type = REG_SZ;
lret = RegQueryValueExA(hkey, "test", NULL, &type, (LPBYTE)&value, &dwvalue);
if (lret == ERROR_SUCCESS)
{
if ((dwvalue > 0) && (value[dwvalue-1] == 0))
--dwvalue;
cout.write(value, dwvalue);
}
RegCloseKey(hkey);
hkey = NULL;
}