Winapi 当缓冲区已经足够大时,RegQueryValueEx返回错误\u更多\u数据

Winapi 当缓冲区已经足够大时,RegQueryValueEx返回错误\u更多\u数据,winapi,registry,getlasterror,Winapi,Registry,Getlasterror,对不起,我的英语太纯正了 我有两个进程可以将数据读写到相同的值(我的测试就是这样做的)。 有时(每十次读取一次)读取方法失败,错误为多个数据,值为12。 但是我用32字节从测试中调用Read方法 碰巧我在watch(GetLastError())中查看了@err,hr,看到了错误\u NOT \u OWNER错误代码。我知道第二个进程是阻止密钥,我必须重试 有人能同意我的结论吗(MSDN没有对此发表任何意见)? 有人能告诉我其他奇怪的效果吗 多谢各位 更新: 我有UAC虚拟化。所有更改都存储到

对不起,我的英语太纯正了

我有两个进程可以将数据读写到相同的值(我的测试就是这样做的)。 有时(每十次读取一次)读取方法失败,错误为多个数据,值为12。 但是我用32字节从测试中调用Read方法

碰巧我在watch(GetLastError())中查看了@err,hr,看到了错误\u NOT \u OWNER错误代码。我知道第二个进程是阻止密钥,我必须重试

有人能同意我的结论吗(MSDN没有对此发表任何意见)? 有人能告诉我其他奇怪的效果吗

多谢各位

更新: 我有UAC虚拟化。所有更改都存储到 [HKEY\U CLASSES\U ROOT\VirtualStore\MACHINE\SOFTWARE] 可能是虚拟化的效果

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