2012-05-08 15 views
6

funkcje GetPrivateProfileXXX Windows' (używane do pracy z plikami INI) mają jakieś dziwne zasady dotyczące postępowania z długości bufora.GetPrivateProfileString - długość bufora

GetPrivateProfileString za stany dokumentacja:

Jeśli [..] dostarczony bufor docelowy jest zbyt mały, aby pomieścić żądany ciąg, ciąg jest obcinany, a następnie znak null, a zwracana jest wartość równa n Rozmiar minus jeden.

Czytam to i zdałem sobie sprawę, że takie zachowanie sprawia, że ​​niemożliwe do rozróżnienia między dwoma scenariuszami w Kod producenta:

  • Kiedy długość wartość ciągu jest dokładnie równa nRozmiar - 1.
  • Kiedy wartość nSize (tj. bufor) jest zbyt mała.

pomyślałem, że eksperyment:

mam to w pliku INI:

[Bar] 
foo=123456 

I zadzwoniłem GetPrivateProfileString z tych argumentów jak test:

// Test 1. The buffer is big enough for the string (16 character buffer). 
BYTE* buffer1 = (BYTE*)calloc(16, 2); // using 2-byte characters ("Unicode") 
DWORD result1 = GetPrivateProfileString(L"Bar", L"foo", NULL, buffer, 16, fileName); 

// result1 is 6 
// buffer1 is { 49, 0, 50, 0, 51, 0, 52, 0, 53, 0, 54, 0, 0, 0, 0, 0, ... , 0, 0 } 

// Test 2. The buffer is exactly sufficient to hold the value and the trailing null (7 characters). 
BYTE* buffer2 = (BYTE*)calloc(7, 2); 
DWORD result2 = GetPrivateProfileString(L"Bar", L"foo", NULL, buffer, 7, fileName); 

// result2 is 6. This is equal to 7-1. 
// buffer2 is { 49, 0, 50, 0, 51, 0, 52, 0, 53, 0, 54, 0, 0, 0 } 

// Test 3. The buffer is insufficient to hold the value and the trailing null (6 characters). 
BYTE* buffer3 = (BYTE*)calloc(6, 2); 
DWORD result3 = GetPrivateProfileString(L"Bar", L"foo", NULL, buffer, 6, fileName); 

// result3 is 5. This is equal to 6-1. 
// buffer3 is { 49, 0, 50, 0, 51, 0, 52, 0, 53, 0, 0, 0 } 

Program wywołujący ten kod nie miałby żadnej pewności, czy rzeczywista wartość klucza ma rzeczywiście 5 znaków długości, czy nawet 6, jak w dwóch ostatnich cas es wynik jest równy nSize - 1.

Jedynym rozwiązaniem jest sprawdzenie, kiedy wynik == nRozmiar - 1 i przywołanie funkcji z większym buforem, ale byłoby to niepotrzebne w przypadkach, w których bufor ma dokładnie taką wartość dobry rozmiar.

Nie istnieje lepszy sposób?

Odpowiedz

5

Nie ma lepszego sposobu. Po prostu spróbuj upewnić się, że pierwszy bufor jest wystarczająco duży. Każda metoda, która rozwiązuje ten problem, musiałaby skorzystać z czegoś nieopisanego w dokumentacji, a zatem nie miałaby gwarancji działania.

1

No, niestety, nie ma lepszego sposobu. Musisz zapewnić wystarczająco duży bufor. Jeśli to nie wystarczy, należy ponownie przydzielić bufor. Wziąłem fragment kodu z here i dostosowane do sprawy:

int nBufferSize = 1000; 
int nRetVal; 
int nCnt = 0; 
BYTE* buffer = (BYTE*)calloc(1, 2); 

do 
{ 
    nCnt++; 
     buffer = (BYTE*) realloc (buffer , nBufferSize * 2 * nCnt); 
     DWORD nRetVal = GetPrivateProfileString(L"Bar", L"foo", NULL,   
      buffer, nBufferSize*nCnt, filename);  
} while((nRetVal == ((nBufferSize*nCnt) - 1)) || 
      (nRetVal == ((nBufferSize*nCnt) - 2))); 

ale w konkretnym przypadku, nazwa pliku nie może mieć długość większą niż MAX_PATH, więc (MAX_PATH+1)*2 będzie zawsze pasuje.

+0

Czy to ma być kod C lub C++? –

0

Może, nazywając GetLastError zaraz po GetPrivateProfileString jest droga. Jeśli bufor jest wystarczająco duży i nie ma innych błędów, GetLastError zwraca 0. Jeśli bufor jest zbyt mały, GetLastError zwraca 234 (0xEA) ERROR_MORE_DATA.

+0

Niestety, funkcje INI zawsze zwracają 'ERROR_MORE_DATA' gdy wypełniają całkowicie bufor (nawet bez obcięcia danych). – efotinis

0

Wiem, że to trochę późno, ale wpadłem niesamowite rozwiązania. Jeśli nie ma już miejsca w buforze (długość powrotu + 1 = długość bufora), zwiększ bufor i uzyskaj ponownie wartość. Powtarzaj ten proces, aż pozostanie miejsce w buforze.