2012-05-08 2 views
6

У функций GetPrivateProfileXXX Windows (используемых для работы с файлами INI) есть некоторые странные правила, касающиеся обработки длин буфера.GetPrivateProfileString - Длина буфера

документация состояния GetPrivateProfileString в:

Если [..] прилагаемый буфер назначения слишком мал, чтобы держать запрошенную строку, строка обрезается и последующим нулевым символом, а возвращаемое значение равно nSize минус один.

Я прочитал это, и я понял, что такое поведение делает невозможным провести различие между двумя сценариями в-коде:

  • Когда длина строковое значение в точности равно nРазмер: - 1.
  • Когда значение nSize (т.е. буфер) слишком мало.

Я думал, что эксперимент:

У меня есть это в файле INI:

[Bar] 
foo=123456 

И я назвал GetPrivateProfileString с этими аргументами, как тест:

// 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 } 

Программа, вызывающая этот код, не сможет точно знать, действительно ли фактическое значение ключа составляет 5 символов или даже 6, как в последних двух случаях Результат равен nSize - 1.

Единственным решением является проверка всякого результата == nSize - 1 и вызов функции с большим буфером, но это было бы необязательно в тех случаях, когда буфер имеет ровно правильный размер.

Нет ли лучшего способа?

ответ

5

Нет лучшего способа. Просто попробуйте убедиться, что первый буфер достаточно велик. Любой метод, который решает эту проблему, должен будет использовать что-то, что не описано в документации, и, следовательно, не будет гарантировать работу.

1

Нет, к сожалению, нет лучшего способа. Вы должны предоставить буфер, достаточно большой. Если этого недостаточно, перераспределите буфер. Я взял фрагмент кода here и адаптированный к вашему делу:

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))); 

, но в вашем конкретном случае, имя файла не может иметь длину более MAX_PATH, так (MAX_PATH+1)*2 всегда будет нужным.

+0

Предполагается, что это код C или C++? –

0

Возможно, позвонив по телефону GetLastError сразу после GetPrivateProfileString - это путь. Если буфер достаточно большой и нет других ошибок, GetLastError возвращает 0. Если буфер слишком мал, GetLastError возвращает 234 (0xEA) ERROR_MORE_DATA.

+0

К сожалению, функции INI всегда возвращают 'ERROR_MORE_DATA', когда они полностью заполняют буфер (даже без усечения данных). – efotinis

0

Я знаю, что немного поздно, но я придумал удивительное решение. Если оставшееся буферное пространство осталось (длина возврата + 1 = длина буфера), затем увеличьте буфер и снова получите значение. Повторите этот процесс до тех пор, пока не останется буферное пространство.

Смежные вопросы