【问题标题】:RegQueryValueEx return ERROR_MORE_DATA when the buffer is already large enough当缓冲区已经足够大时,RegQueryValueEx 返回 ERROR_MORE_DATA
【发布时间】:2013-01-23 06:18:02
【问题描述】:

对不起,我的纯英语。

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

偶然我查看了@err,hr in watch (GetLastError()) 并看到了 ERROR_NOT_OWNER 错误代码。我知道第二个进程阻塞了密钥,我必须再试一次。

任何人都可以批准我的结论(MSDN 对此没有说什么)? 谁能告诉我其他奇怪的效果?

谢谢。

更新: 我有 UAC 虚拟化。所有更改都存储到 [HKEY_CLASSES_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;
}

【问题讨论】:

    标签: winapi registry getlasterror


    【解决方案1】:

    注册表函数不使用GetLastError() 报告错误。它们直接返回错误代码。所以ERROR_NOT_OWNER 具有误导性,它来自早期的 Win32 API 调用,而不是注册表调用。

    您不可能将大小值32 传递给RegQueryValueEx() 并返回一个ERROR_MORE_DATA 错误,指出数据的大小实际上是12RegQueryValueEx() 不能那样工作。确保您的 Size 值在进入 Read() 函数时实际设置为 32,而不是设置为其他值。

    更新:不过,RegQueryValueEx() 可以报告 ERROR_MORE_DATA 并返回两倍于您请求的数据大小,即使 RegSetValueEx() 实际上不是通过了那么多数据。当我运行您的测试代码时,我能够让RegQueryValueEx() 有时(不是每次)报告数据大小为 64,即使请求的是 32。原因是您的代码实际调用的 RegSetValueExA() 执行从 Ansi 到 Unicode 的字符串类型(REG_SZREG_MULTI_SZREG_EXPAND_SZ)和 RegQueryValueExA() 的数据转换,您的代码实际上是调用、查询原始字节并对字符串类型执行 Unicode 到 Ansi 的转换。因此,虽然您的编写代码可能会保存 32 个 char 值,因此是 32 个字节,但注册表实际上是存储 32 个 wchar_t 值,因此是 64 个字节(如果您的输入字符串中包含非 ASCII 字符会更多) .很有可能,RegQueryValueEx() 按原样返回原始字节而不是转换它们,例如,如果 RegSetValueEx() 先保存原始字节,然后再保存数据类型,但 RegQueryValueEx() 在数据类型已保存,因此不知道该数据是需要转换的字符串类型。

    无论哪种方式,这是一个线程/进程读取而另一个线程/进程正在写入之间的竞争条件,写入在刷新数据之前在内部缓存数据时出现读取问题等。除非您同步,否则您无能为力读取和写入,因为 Registry API 不会为您同步。

    【讨论】:

    • 谢谢。 GetLastError() 真正由另一个代码存储。但我修改代码以记住输入参数并显示输出。他们还是一样的:
    • 查询前:尺寸:32 查询后:必填尺寸:12 结果:234
    • 你所描述的在正常情况下应该是不可能的,所以我不得不怀疑你不是在正常情况下。例如,您是否在非 Windows PC 上的虚拟化环境(VirtualPC、VMWare、Wine 等)中运行您的应用程序?某些东西可能会钩住RegQueryValueEx() 并改变其正常行为。单独的 UAC 不会这样做。
    • Windows 7 Professional SP 1. 不是任何虚拟应用程序。应用程序具有 32 位架构。只有这样。第一个过程是写密钥。第二个进程尝试同时读取它。密钥在任何更改之前就存在。
    【解决方案2】:

    我为我的问题编写示例。我在第三次开始时重复了它。

    如果样本是复杂的,您可以看到“查询复杂”和“SetComplite”消息

    在 err 上你应该看到:“error more data: ??”

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

    【讨论】:

    • 我在 Win7 Home SP1 64bit 上将代码作为 32bit 运行。我没有收到任何错误。我调整了代码,因此主进程将背靠背产生无穷无尽的子进程。没有错误。编写程序完成编写过程大约需要 30 秒。在那个时间范围内,产生了 10-11 个阅读器进程并完成了它们的阅读。我让代码运行了 5 分钟。没有错误。我回到原始代码,调整子进程以无限循环运行而不是退出。几乎立即得到错误。再次运行相同的代码。没有错误。再跑一遍,报错了,但是报告的大小是64!
    • 所以这很可能是一个线程/进程读取而另一个线程/进程正在写入之间的时间问题,写入在刷新数据之前在内部缓存数据时出现读取问题,等等。除非您同步读取和写入,否则可以这样做,因为 Registry API 没有为您同步。如果我将代码更改为使用RegOpenKeyEx(),指定KEY_QUERY_VALUEKEY_SET_VALUE 权限,我会多次收到错误,每次都报告64 的大小。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-04
    • 1970-01-01
    • 2017-05-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-17
    相关资源
    最近更新 更多