我正在尝试读取注册表项值并显示它。我创建了自己当前正在尝试读取的密钥,并使用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
中?如果是,为什么每次都得到不同的输出?
答案 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[]
话虽如此,您的代码也存在其他一些问题。
您使用的是基于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
存在。
您对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()
,以确保在输出中始终有一个空终止符,即使源数据中不存在该终止符也是如此。
无论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;
}