Winapi 当缓冲区已经足够大时,RegQueryValueEx返回错误\u更多\u数据
对不起,我的英语太纯正了 我有两个进程可以将数据读写到相同的值(我的测试就是这样做的)。 有时(每十次读取一次)读取方法失败,错误为多个数据,值为12。 但是我用32字节从测试中调用Read方法 碰巧我在watch(GetLastError())中查看了@err,hr,看到了错误\u NOT \u OWNER错误代码。我知道第二个进程是阻止密钥,我必须重试 有人能同意我的结论吗(MSDN没有对此发表任何意见)? 有人能告诉我其他奇怪的效果吗 多谢各位 更新: 我有UAC虚拟化。所有更改都存储到 [HKEY\U CLASSES\U ROOT\VirtualStore\MACHINE\SOFTWARE] 可能是虚拟化的效果Winapi 当缓冲区已经足够大时,RegQueryValueEx返回错误\u更多\u数据,winapi,registry,getlasterror,Winapi,Registry,Getlasterror,对不起,我的英语太纯正了 我有两个进程可以将数据读写到相同的值(我的测试就是这样做的)。 有时(每十次读取一次)读取方法失败,错误为多个数据,值为12。 但是我用32字节从测试中调用Read方法 碰巧我在watch(GetLastError())中查看了@err,hr,看到了错误\u NOT \u OWNER错误代码。我知道第二个进程是阻止密钥,我必须重试 有人能同意我的结论吗(MSDN没有对此发表任何意见)? 有人能告诉我其他奇怪的效果吗 多谢各位 更新: 我有UAC虚拟化。所有更改都存储到
{
...
char name[32] = "";
grandchild.OpenValue("name").Read(name, _countof(name));
...
}
bool RegisteryStorageValue::Read(void* Buffer, size_t Size) throw (IOException)
{
DWORD Value = DWORD(Size);
DWORD rez = ::RegQueryValueEx(mKey, mName.c_str(), NULL, NULL, (BYTE*)Buffer, &Value);
if (rez != ERROR_SUCCESS) // here I have 'rez = ERROR_MORE_DATA' and 'Value = 12'
throw IOException(rez);
return true;
}
bool RegisteryStorageValue::Write(Type type, const void* Buffer, size_t Size) throw (IOException)
{
DWORD rez = ::RegSetValueEx(mKey, mName.c_str(), NULL, getRegType(type), (const BYTE*)Buffer, (DWORD)Size);
if (rez != ERROR_SUCCESS)
throw IOException(rez);
return true;
}
注册表函数不使用
GetLastError()
报告错误。它们直接返回错误代码。因此错误\u NOT \u OWNER
具有误导性,它来自早期的Win32 API调用,而不是注册表调用
您无法将大小值32
传递到RegQueryValueEx()
,并返回一个错误\u更多数据
错误,表示数据的大小实际上是12
RegQueryValueEx()
不是这样工作的。确保在进入Read()
函数时,您的Size
值实际设置为32,而不是设置为其他值
更新:但是,RegQueryValueEx()
可以报告ERROR\u MORE\u DATA
并返回比您请求的数据大两倍的数据大小,即使RegSetValueEx()
实际上没有传递那么多数据。当我运行您的测试代码时,我能够让RegQueryValueEx()
有时(不是每次)报告64个数据大小,即使请求了32个。原因是,代码实际调用的RegSetValueExA()
,对字符串类型(REG\u SZ
、REG\u MULTI\u SZ
和REG\u EXPAND\u SZ
)和代码实际调用的RegQueryValueExA()
)执行从Ansi到Unicode的数据转换,查询原始字节并对字符串类型执行Unicode到Ansi的转换。因此,虽然您编写的代码可能保存了32个char
值,即32个字节,但注册表实际上存储的是32个wchar\t
值,即64个字节(如果您的输入字符串中包含非ASCII字符,则会更多)。很可能,RegQueryValueEx()
会按原样返回原始字节,而不是转换它们,例如RegSetValueEx()
会先保存原始字节,然后再保存数据类型,但是RegQueryValueEx()
在保存数据类型之前读取原始字节,因此不知道数据是需要转换的字符串类型
无论哪种方式,这都是一个线程/进程读取而另一个线程/进程写入之间的竞争条件,写入时读取问题会在刷新数据之前在内部缓存数据,等等。除非同步读取和写入,否则无法执行此操作,因为注册表API不为您同步。注册表函数不使用
GetLastError()
报告错误。它们直接返回错误代码。因此错误\u NOT \u OWNER
具有误导性,它来自早期的Win32 API调用,而不是注册表调用
您无法将大小值32
传递到RegQueryValueEx()
,并返回一个错误\u更多数据
错误,表示数据的大小实际上是12
RegQueryValueEx()
不是这样工作的。确保在进入Read()
函数时,您的Size
值实际设置为32,而不是设置为其他值
更新:但是,RegQueryValueEx()
可以报告ERROR\u MORE\u DATA
并返回比您请求的数据大两倍的数据大小,即使RegSetValueEx()
实际上没有传递那么多数据。当我运行您的测试代码时,我能够让RegQueryValueEx()
有时(不是每次)报告64个数据大小,即使请求了32个。原因是,代码实际调用的RegSetValueExA()
,对字符串类型(REG\u SZ
、REG\u MULTI\u SZ
和REG\u EXPAND\u SZ
)和代码实际调用的RegQueryValueExA()
)执行从Ansi到Unicode的数据转换,查询原始字节并对字符串类型执行Unicode到Ansi的转换。因此,虽然您编写的代码可能保存了32个char
值,即32个字节,但注册表实际上存储的是32个wchar\t
值,即64个字节(如果您的输入字符串中包含非ASCII字符,则会更多)。很可能,RegQueryValueEx()
会按原样返回原始字节,而不是转换它们,例如RegSetValueEx()
会先保存原始字节,然后再保存数据类型,但是RegQueryValueEx()
在保存数据类型之前读取原始字节,因此不知道数据是需要转换的字符串类型
无论哪种方式,这都是一个线程/进程读取而另一个线程/进程写入之间的竞争条件,写入时读取问题会在刷新数据之前在内部缓存数据,等等。除非同步读取和写入,否则无法执行此操作,因为注册表API没有为您同步。我为我的问题编写了示例。我在第三次开始时重复了这个问题 如果示例是complite,您可以看到“querycomplite”和“SetComplite”消息 在err上,您应该看到:“错误更多数据:??”
#包括
#包括
#
#include <string>
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
bool start(char* path, char* args)
{
std::string cmd = path;
cmd.push_back(' ');
cmd.append(args);
STARTUPINFO si = {0};
PROCESS_INFORMATION pi = {0};
BOOL res = ::CreateProcess(NULL, (LPSTR)cmd.c_str(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
if (res == FALSE)
return false;
::CloseHandle(pi.hProcess);
::CloseHandle(pi.hThread);
return true;
}
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE hEvent = ::CreateEvent(NULL, TRUE, FALSE, "Local/blah");
if (argc == 1)
{
HKEY hKey;
if (::RegCreateKey(HKEY_CURRENT_USER, "Software\\TestRegistry", &hKey) != ERROR_SUCCESS)
return -1;
char buffer[] = "Hello, Stack!";
::RegSetValueEx(hKey, "Value", 0, REG_SZ, (BYTE*)buffer, _countof(buffer));
::RegCloseKey(hKey);
if (start(argv[0], "r") == false ||
start(argv[0], "w") == false)
return -2;
::Sleep(1000);
::SetEvent(hEvent);
}
else
{
if (argv[1][0] == 'r')
{
HKEY hKey;
if (::RegOpenKey(HKEY_CURRENT_USER, "Software\\TestRegistry", &hKey) != ERROR_SUCCESS)
return -1;
char buffer[1024] = {0};
if (::WaitForSingleObject(hEvent, 10000) == WAIT_TIMEOUT)
return -3;
for (size_t index = 0; index < 1000000; ++index)
{
DWORD dwType;
DWORD dwSize = _countof(buffer);
DWORD result = ::RegQueryValueEx(hKey, "Value", 0, &dwType, (LPBYTE)buffer, &dwSize);
if (result == ERROR_SUCCESS)
continue;
if (result == ERROR_MORE_DATA)
{
::printf_s("\nError more data: %d\n", dwSize);
return 1;
}
}
::RegCloseKey(hKey);
::printf_s("\nQuery completed\n");
}
else
{
::srand(::GetTickCount());
HKEY hKey;
if (::RegOpenKey(HKEY_CURRENT_USER, "Software\\TestRegistry", &hKey) != ERROR_SUCCESS)
return -1;
const size_t word_size = 32;
char dict[][word_size] =
{
"aaaaaaaa",
"help me",
"rape me",
"in the pines",
"argh",
};
char buffer[1024] = {0};
if (::WaitForSingleObject(hEvent, 10000) == WAIT_TIMEOUT)
return -3;
for (size_t index = 0; index < 1000000; ++index)
{
DWORD dwType = REG_SZ;
DWORD dwSize = word_size;
DWORD result = ::RegSetValueEx(hKey, "Value", 0, dwType, (LPBYTE)dict[rand() % _countof(dict)], dwSize);
if (result == ERROR_SUCCESS)
continue;
}
::RegCloseKey(hKey);
::printf_s("\nSet completed\n");
}
}
return 0;
}