RegQueryValueEx每次都返回不同的值

时间:2018-07-12 17:44:59

标签: c++ registry

我正在尝试读取注册表项值并显示它。我创建了自己当前正在尝试读取的密钥,并使用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中?如果是,为什么每次都得到不同的输出?

1 个答案:

答案 0 :(得分:0)

您的代码显然正在针对Unicode进行编译,因此TCHAR映射到wchar_t,而RegQueryValueEx()映射到RegQueryValueExW()

但是std::cout的{​​{1}}指针没有operator<<作为输入,但是它的wchar_t*指针却有operator<<,可以打印指针中包含的内存地址。

任何指针都可以隐式转换为void*,因此实际上就是调用的void*重载,这就是为什么在输出中看到十六进制数字的原因。

因为您的overator<<缓冲区每次都位于不同的内存地址,所以每次运行代码都将获得不同的值。

要对此进行说明,您需要:

  • 在定义value[]时使用std::wcout代替std::cout

    UNICODE

    或者:

    #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()输出: / p>

    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[]

话虽如此,您的代码也存在其他一些问题。

  1. 您使用的是基于char value[1024]; ... RegQueryValueExA(..., (LPBYTE)&value, ...); ... cout << value; 的API,但是您使用的是硬编码的字符串文字。仅当您将项目设置为针对Unicode编译(应该是Unicode)时,这才起作用。但是要从政治上正确,在通过TCHAR API使用字符串文字时,应该使用TEXT()宏。

    TCHAR

    但是,您真的不应该再使用TEXT("Software\\Classes\\foo") TEXT("test") ... API。在Windows仍是向Unicode迁移的基于ANSI的操作系统的早期就引入了它们。 TCHAR帮助开发人员支持操作系统的ANSI和Unicode版本。但是那些日子已经一去不复返了。除非您绝对需要ANSI API,否则请仅使用Unicode API。忘记TCHAR存在。

  2. 您对TCHAR的呼叫完全错误。

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

      value
    • 您不能在其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"), ...); ... } 参数传递了错误的大小值。该参数以字节表示,但是您要传递的大小以字符表示。而且由于您的代码是针对Unicode编译的,因此lpcbData为2,这意味着sizeof(TCHAR)只能写入缓冲区的一半。使用RegQueryValueEx()来获取完整的字节大小:

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

  3. 无论RegQueryValueEx()是成功还是失败,您都将value缓冲区传递给std::cout。如果RegQueryValueEx()失败,则缓冲区的内容将是不确定的,并且不能保证以null结尾,因此完全不要使用缓冲区。

请尝试以下类似操作:

RegQueryValueEx()

或者:

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;
}