Question

Sorry for my pure english.

I have two processes which can Read and Write data to the same value(my tests do it). Sometimes(one per ten times) the read method is fail with error ERROR_MORE_DATA and Value is 12. But I call Read method from the tests with 32 bytes.

By chance I looked to @err,hr in watch (GetLastError()) and saw ERROR_NOT_OWNER error code.I understand that second process is block the key and I must try again.

Can anybody approve my conclusions (MSDN is not say anything about this)? Can anybody will tell me other strange effects?

Thank you.

Update: I have UAC Virtualization. All changes are stored to the [HKEY_CLASSES_ROOT\VirtualStore\MACHINE\SOFTWARE] May be it is effect virtualization???

{
...
    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;
}
Était-ce utile?

La solution

Registry functions do not use GetLastError() to report errors. They return error codes directly. So the ERROR_NOT_OWNER is misleading, it is from an earlier Win32 API call, not the Registry calls.

There is no possible way you can pass in a Size value of 32 to RegQueryValueEx() and get back an ERROR_MORE_DATA error saying the data is actually 12 in size. RegQueryValueEx() does not work that way. Make sure your Size value is actually set to 32 upon entry to the Read() function and is not set to some other value.

Update: it is, however, possible for RegQueryValueEx() to report ERROR_MORE_DATA and return a data size that is twice as larger as what you requested, even if RegSetValueEx() is not actually passed that much data. When I ran your test code, I was able to get RegQueryValueEx() to sometimes (not every time) report a data size of 64 even though 32 was being requested. The reason is because RegSetValueExA(), which your code is actually calling, performs a data conversion from Ansi to Unicode for string types (REG_SZ, REG_MULTI_SZ and REG_EXPAND_SZ), and RegQueryValueExA(), which your code is actually calling, queries the raw bytes and performs a Unicode to Ansi conversion for string types. So while your writing code may be saving 32 char values, thus 32 bytes, the Registry is actually storing 32 wchar_t values instead, thus 64 bytes (it would be more if your input strings had non-ASCII characters in them). Chances are, RegQueryValueEx() is returning the raw bytes as-is instead of converting them, such as if RegSetValueEx() is saving the raw bytes first and then saving the data type afterwards, but RegQueryValueEx() is reading the raw bytes before the data type has been saved and thus does not know the data is a string type that needs converting.

Either way, this is a race condition between one thread/process reading while another thread/process is writing, issues with reading while the writing is caching data internally before flushing it, etc. Nothing you can do about this unless you synchronize the reads and writes, since the Registry API does not synchronize for you.

Autres conseils

I write sample for my question. I have repeate it issue on the third start.

If sample is complite you can see "Query complite" and "SetComplite" messages

On err you should saw: "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;
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top