2013-01-23 3 views
1

내 순수 영어로 죄송합니다.버퍼가 이미 충분히 큰 경우 RegQueryValueEx가 ERROR_MORE_DATA를 반환합니다.

동일한 값으로 데이터를 읽고 쓸 수있는 두 개의 프로세스가 있습니다 (테스트에서 수행). 때때로 (10 회에 1 회) read 메소드가 ERROR_MORE_DATA 오류이고 값이 12 인 경우 실패합니다. 그러나 32 바이트 테스트에서 Read 메소드를 호출합니다.

우연히 watch (GetLastError())에서 @ err, hr을보고 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; 
} 

답변

1

레지스트리 함수는 GetLastError()을 사용하여 오류를보고하지 않습니다. 오류 코드를 직접 반환합니다. 따라서 ERROR_NOT_OWNER은 오해의 소지가 있습니다. 레지스트리 호출이 아니라 이전 Win32 API 호출에서 온 것입니다.

RegQueryValueEx()32의 크기 값을 전달하고 데이터의 크기가 실제로 12입니다 말하는 ERROR_MORE_DATA 오류를 다시 얻을 수있는 가능한 방법이 없다. RegQueryValueEx() 그런 식으로 작동하지 않습니다. Size 값이 실제로 Read() 기능으로 들어가면 32로 설정되고 다른 값으로 설정되지 않았는지 확인하십시오.

업데이트 :RegQueryValueEx()RegSetValueEx()이 실제로 많은 데이터를 전달되지 않은 경우에도, ERROR_MORE_DATA를보고 두 번 요청한 것과 큰으로하는 데이터 크기를 반환하는 것은, 그러나 가능하다. 테스트 코드를 실행할 때마다 32 개가 요청되었지만 때로는 RegQueryValueEx()에 64 개 데이터 크기를보고 할 수있었습니다. 코드가 실제로 호출하는 RegSetValueExA()이 코드에서 실제로 호출하는 문자열 유형 (REG_SZ, REG_MULTI_SZREG_EXPAND_SZ) 및 RegQueryValueExA()에 대한 데이터 변환을 수행하고 원시 바이트를 쿼리하고 유니 코드를 수행하기 때문입니다 문자열 형식에 대한 Ansi 변환 따라서 작성 코드가 32 char 값을 저장하는 동안 32 바이트이므로 레지스트리는 실제로 32 wchar_t 값을 저장하므로 64 바이트가됩니다 (입력 문자열에 비 ASCII 문자가있는 경우 더 많을 것입니다). 가능성이있는 경우 RegQueryValueEx()RegSetValueEx()이 원시 바이트를 먼저 저장하고 나중에 데이터 유형을 저장하는 경우와 같이 원시 바이트를 변환하는 대신 그대로 반환하지만 RegQueryValueEx()은 데이터 유형이 저장되기 전에 원시 바이트를 읽는 것입니다. 따라서 데이터가 변환이 필요한 문자열 유형인지는 알 수 없습니다.

어느 쪽이든, 이것은 다른 스레드/프로세스가 쓰는 동안 한 스레드/프로세스 읽기 사이의 경쟁 조건이며, 쓰기가 플러시하기 전에 내부적으로 데이터를 캐시하는 동안 읽기와 관련된 문제입니다. 레지스트리 API가 동기화되지 않기 때문에 읽기와 쓰기를 동기화하십시오.

+0

감사합니다. GetLastError()는 실제로 다른 코드에 의해 저장됩니다. 그러나 코드를 수정하여 입력 매개 변수를 기억하고 with 출력을 dispay합니다. 그리고 그들은 여전히 ​​동일합니다 : – diman

+0

쿼리하기 전에 : \t 크기 : 쿼리 후 32 : \t 필수 크기 : 12 \t 결과 : 234 – diman

+0

당신이 정상적인 조건에서 가능하지 않아야 설명, 그래서 당신이 의심해야 정상적인 상황이 아닙니다. 비 Windows PC에서 가상화 된 환경 (VirtualPC, VMWare, Wine 등)에서 앱을 실행하고 있습니까? 뭔가가'RegQueryValueEx()'에 연결되어 정상 동작을 변경합니다. UAC만으로는 그렇게하지 않을 것입니다. –

0

나는 내 질문에 대한 샘플을 씁니다.나는 세 번째 시작에서 문제를 되풀이했다. 당신이 "쿼리 complite"와 "SetComplite"메시지 당신이해야 ERR에

보았다 볼 수 있습니다 complite

샘플 인 경우 : "오류 더 많은 데이터를 : ??"

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

Win7 Home SP1 64 비트에서 32 비트로 코드를 실행했습니다. 나는 어떤 오류도 내지 않았다. 주 프로세스가 끝이없는 자식 프로세스를 연속적으로 생성하도록 코드를 수정했습니다. 오류 없음. 작가 프로세스가 글쓰기를 끝내는 데 30 초 정도 걸렸습니다. 그 기간 동안 10-11 명의 리더 프로세스가 생성되어 독서가 완료되었습니다. 5 분 동안 코드를 실행 시켰습니다. 오류 없음. 원래 코드로 돌아가서 자식 프로세스를 끝내는 대신 무한 루프에서 실행하도록 수정했습니다. 거의 즉시 오류가 발생했습니다. 동일한 코드를 다시 실행하십시오. 오류 없음. 다시 실행 해보고 오류가 발생했지만보고 된 크기는'64 '입니다! –

+0

그래서 다른 스레드/프로세스가 쓰는 동안 한 스레드/프로세스 읽기 사이의 타이밍 문제가 될 수 있습니다. 쓰기 작업이 플러시되기 전에 내부적으로 데이터를 캐싱하는 동안 읽기 문제가 있습니다. 동기화 할 때까지는 아무 것도 할 수 없습니다 레지스트리 API가 동기화되지 않기 때문에 읽기 및 쓰기가 필요합니다. 'RegOpenKeyEx()'대신'KEY_QUERY_VALUE'와'KEY_SET_VALUE' 권한을 사용하도록 코드를 변경하면, 매번'64 '크기를보고 할 때마다 오류가 몇 번 더 발생합니다. –