【问题标题】:GetPrivateProfileString - Buffer lengthGetPrivateProfileString - 缓冲区长度
【发布时间】:2025-12-19 19:40:15
【问题描述】:

Windows 的 GetPrivateProfileXXX 函数(用于处理 INI 文件)在处理缓冲区长度方面有一些奇怪的规则。

GetPrivateProfileString 的文档说明:

如果 [..] 提供的目标缓冲区太小而无法容纳请求的字符串,则该字符串被截断并后跟一个空字符,并且返回值等于 nSize 减一。

我读了这篇文章,我意识到这种行为使得无法区分代码中的两个场景:

  • 当值字符串的长度正好等于 nSize - 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。

唯一的解决方案是在 result == nSize - 1 时检查并使用更大的缓冲区调用函数,但在缓冲区大小正好合适的情况下,这是不必要的。

没有更好的办法吗?

【问题讨论】:

    标签: winapi ini


    【解决方案1】:

    没有更好的方法。只需尝试确保第一个缓冲区足够大。任何解决此问题的方法都必须使用文档中未描述的内容,因此无法保证有效。

    【讨论】:

      【解决方案2】:

      当我致力于将一些古老的代码带入未来时,我发现了这个关于缓冲和 Private Profile API 的问题。经过我自己的实验和研究,我可以确认提问者关于无法确定字符串恰好为 nSize - 1 或缓冲区太小之间的差异的原始陈述。

      有没有更好的方法? Mike 接受的回答说没有根据文档,您应该尝试以确保缓冲区足够大。 Marc 说要增加缓冲区。 Roman 说检查错误代码。一些随机用户说您需要提供一个足够大的缓冲区,并且与 Marc 不同的是,他继续展示一些扩展他的缓冲区的代码。

      有没有更好的方法?让我们了解事实!

      由于 ProfileString API 的年代久远,因为这个问题的所有标签都不涉及任何特定语言并且为了便于阅读,我决定使用 VB6 来展示我的示例。随意翻译它们以用于您自己的目的。


      GetPrivateProfileString 文档

      根据GetPrivateProfileString documentation,提供这些私有配置文件功能只是为了与基于 Windows 的 16 位应用程序兼容。这是很好的信息,因为它使我们能够了解这些 API 函数的局限性。

      16 位有符号整数的范围为 -32,768 到 32,767,无符号 16 位整数的范围为 0 到 65,535。如果这些函数真的是为在 16 位环境中使用而设计的,那么我们遇到的任何数字很可能都会被限制在这两个限制之一。

      文档指出,返回的每个字符串都将以空字符结尾,并且还说不适合提供的缓冲区的字符串将被截断并以空字符终止。因此,如果一个字符串确实适合缓冲区,则倒数第二个字符以及最后一个字符都将为空。如果只有最后一个字符为 null,则提取的字符串与提供的缓冲区的长度完全相同 - 1 或者缓冲区不够大,无法容纳字符串。

      在倒数第二个字符不为空、提取的字符串的长度准确或对于缓冲区来说太大的情况下,GetLastError 将返回错误号 234 ERROR_MORE_DATA (0xEA),让我们无法区分它们。


      GetPrivateProfileString 接受的最大缓冲区大小是多少?

      虽然文档没有说明最大缓冲区大小,但我们已经知道此 API 是为 16 位环境设计的。经过一些实验,我可以得出结论,最大缓冲区大小为 65,536。如果文件中的字符串大于 65,535 个字符,我们会在尝试读取字符串时开始看到一些奇怪的行为。如果文件中的字符串长度为 65,536 个字符,则检索到的字符串长度为 0 个字符。如果文件中的字符串长度为 65,546 个字符,则检索到的字符串长度为 10 个字符,以空字符结尾并从文件中包含的字符串的最开头截断。 API 将写入大于 65,535 个字符的字符串,但无法读取大于 65,535 个字符的任何内容。如果缓冲区长度为 65,536 且文件中的字符串长度为 65,535 个字符,则缓冲区将包含文件中的字符串,并以单个空字符结尾。

      这为我们提供了第一个虽然不是完美的解决方案。如果您想始终确保您的第一个缓冲区足够大,请将该缓冲区设置为 65,536 个字符长。

      Private Declare Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Long, ByVal lpFileName As String) As Long
      
      Public Function iniRead(ByVal Pathname As String, ByVal Section As String, ByVal Key As String, Optional ByVal Default As String) As String
          On Error GoTo iniReadError
          Dim Buffer As String
          Dim Result As Long
          Buffer = String$(65536, vbNullChar)
          Result = GetPrivateProfileString(Section, Key, vbNullString, Buffer, 65536, Pathname)
          If Result <> 0 Then
              iniRead = Left$(Buffer, Result)
          Else
              iniRead = Default
          End If
      iniReadError:
      End Function
      

      现在我们知道了最大缓冲区大小,我们可以使用文件大小来修改它。如果您的文件长度小于 65,535 个字符,则可能没有理由创建如此大的缓冲区。

      在文档的备注部分它说初始化文件中的一个部分必须具有以下形式:

      [部分]
      key=string

      我们可以假设每个部分都包含两个方括号和一个等号。经过一个小测试,我能够验证 API 是否可以接受节和键之间的任何类型的换行符(vbLf、vbCr 或 vbCrLf / vbNewLine)。这些细节以及节和键名的长度将允许我们缩小最大缓冲区长度,并确保文件大小足以在我们尝试读取文件之前包含字符串。

      Private Declare Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Long, ByVal lpFileName As String) As Long
      
      Public Function iniRead(ByVal Pathname As String, ByVal Section As String, ByVal Key As String, Optional ByVal Default As String) As String
          On Error Resume Next
          Dim Buffer_Size As Long
          Err.Clear
          Buffer_Size = FileLen(Pathname)
          On Error GoTo iniReadError
          If Err.Number = 0 Then
              If Buffer_Size > 4 + Len(Section) + Len(Key) Then
                  Dim Buffer As String
                  Dim Result As Long
                  Buffer_Size = Buffer_Size - Len(Section) - Len(Key) - 4
                  If Buffer_Size > 65535 Then
                      Buffer_Size = 65536
                  Else
                      Buffer_Size = Buffer_Size + 1
                  End If
                  Buffer = String$(Buffer_Size, vbNullChar)
                  Result = GetPrivateProfileString(Section, Key, vbNullString, Buffer, Buffer_Size, Pathname)
                  If Result <> 0 Then
                      iniRead = Left$(Buffer, Result)
                      Exit Function
                  End If
              End If
          End If
          iniRead = Default
      iniReadError:
      End Function
      

      增加缓冲区

      既然我们已经非常努力地确保第一个缓冲区足够大,并且我们已经修改了最大缓冲区大小,那么从较小的缓冲区开始并逐渐增加缓冲区的大小仍然可能更有意义buffer 来创建一个足够大的缓冲区,以便我们可以从文件中提取整个字符串。根据文档,API 会创建 234 错误来告诉我们还有更多可用数据。他们使用这个错误代码告诉我们用更大的缓冲区重试是很有意义的。一遍又一遍地重试的缺点是它的成本更高。文件中的字符串越长,读取它所需的尝试次数越多,花费的时间就越长。 64 KB 对于今天的计算机来说并不算多,而且今天的计算机速度非常快,因此您可能会发现这些示例中的任何一个都适合您的目的。

      我对 GetPrivateProfileString API 进行了相当多的搜索,我发现通常情况下,当没有广泛了解 API 的人尝试创建足够大的缓冲区以满足他们的需求时,他们会选择 255 的缓冲区长度。这将允许您从文件中读取长达 254 个字符的字符串。我不确定为什么有人开始使用它,但我假设有人在某个地方想象这个 API 使用缓冲区长度限制为 8 位无符号数的字符串。可能这是WIN16的限制吧。

      除非最大缓冲区长度更小,否则我将从 64 字节的低缓冲区开始,然后将数字翻两番,直到最大缓冲区长度或 65,536。将数字加倍也是可以接受的,更大的乘法意味着更少尝试读取更大字符串的文件,而相对而言,一些中等长度的字符串可能有额外的填充。

      Private Declare Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Long, ByVal lpFileName As String) As Long
      
      Public Function iniRead(ByVal Pathname As String, ByVal Section As String, ByVal Key As String, Optional ByVal Default As String) As String
          On Error Resume Next
          Dim Buffer_Max As Long
          Err.Clear
          Buffer_Max = FileLen(Pathname)
          On Error GoTo iniReadError
          If Err.Number = 0 Then
              If Buffer_Max > 4 + Len(Section) + Len(Key) Then
                  Dim Buffer As String
                  Dim Result As Long
                  Dim Buffer_Size As Long
                  Buffer_Max = Buffer_Max - Len(Section) - Len(Key) - 4
                  If Buffer_Max > 65535 Then
                      Buffer_Max = 65536
                  Else
                      Buffer_Max = Buffer_Max + 1
                  End If
                  If Buffer_Max < 64 Then
                      Buffer_Size = Buffer_Max
                  Else
                      Buffer_Size = 64
                  End If
                  Buffer = String$(Buffer_Size, vbNullChar)
                  Result = GetPrivateProfileString(Section, Key, vbNullString, Buffer, Buffer_Size, Pathname)
                  If Result <> 0 Then
                      If Buffer_Max > 64 Then
                          Do While Result = Buffer_Size - 1 And Buffer_Size < Buffer_Max
                              Buffer_Size = Buffer_Size * 4
                              If Buffer_Size > Buffer_Max Then
                                  Buffer_Size = Buffer_Max
                              End If
                              Buffer = String$(Buffer_Size, vbNullChar)
                              Result = GetPrivateProfileString(Section, Key, vbNullString, Buffer, Buffer_Size, Pathname)
                          Loop
                      End If
                      iniRead = Left$(Buffer, Result)
                      Exit Function
                  End If
              End If
          End If
          iniRead = Default
      iniReadError:
      End Function
      

      改进的验证

      根据您的实现,改进对路径名、部分和键名的验证可能会阻止您需要准备缓冲区。

      根据Wikipedia's INI File page,他们说:

      在 Windows 实现中,键不能包含字符 等号 ( = ) 或分号 ( ; ) 因为这些是保留字符。 该值可以包含任何字符。

      在 Windows 实现中,该部分不能包含字符 右括号 ( ] )。

      对 GetPrivateProfileString API 的快速测试证明这只是部分正确。只要分号不在开头,我就可以在键名中使用分号。他们没有在文档或*中提及任何其他限制,尽管可能还有更多限制。

      另一个快速测试,以查找 GetPrivateProfileString 接受的部分或键名的最大长度,给了我 65,535 个字符的限制。使用大于 65,535 个字符的字符串的效果与我在测试最大缓冲区长度时的体验相同。另一项测试证明,此 API 将接受一个空白字符串作为部分或键名。根据 API 的功能,这是一个可接受的初始化文件:

      []
      =世界你好!

      根据 Wikipedia,对空格的解释各不相同。在又一次测试之后,Profile String API 肯定会从部分和键名中删除空格,所以如果我们也这样做可能没问题。

      Private Declare Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Long, ByVal lpFileName As String) As Long
      
      Public Function iniRead(ByVal Pathname As String, ByVal Section As String, ByVal Key As String, Optional ByVal Default As String) As String
          On Error Resume Next
          If Len(Pathname) <> 0 Then
              Key = Trim$(Key)
              If InStr(1, Key, ";") <> 1 Then
                  Section = Trim$(Section)
                  If Len(Section) > 65535 Then
                      Section = RTrim$(Left$(Section, 65535))
                  End If
                  If InStr(1, Section, "]") = 0 Then
                      If Len(Key) > 65535 Then
                          Key = RTrim$(Left$(Key, 65535))
                      End If
                      If InStr(1, Key, "=") = 0 Then
                          Dim Buffer_Max As Long
                          Err.Clear
                          Buffer_Max = FileLen(Pathname)
                          On Error GoTo iniReadError
                          If Err.Number = 0 Then
                              If Buffer_Max > 4 + Len(Section) + Len(Key) Then
                                  Dim Buffer As String
                                  Dim Result As Long
                                  Dim Buffer_Size As Long
                                  Buffer_Max = Buffer_Max - Len(Section) - Len(Key) - 4
                                  If Buffer_Max > 65535 Then
                                      Buffer_Max = 65536
                                  Else
                                      Buffer_Max = Buffer_Max + 1
                                  End If
                                  If Buffer_Max < 64 Then
                                      Buffer_Size = Buffer_Max
                                  Else
                                      Buffer_Size = 64
                                  End If
                                  Buffer = String$(Buffer_Size, vbNullChar)
                                  Result = GetPrivateProfileString(Section, Key, vbNullString, Buffer, Buffer_Size, Pathname)
                                  If Result <> 0 Then
                                      If Buffer_Max > 64 Then
                                          Do While Result = Buffer_Size - 1 And Buffer_Size < Buffer_Max
                                              Buffer_Size = Buffer_Size * 4
                                              If Buffer_Size > Buffer_Max Then
                                                  Buffer_Size = Buffer_Max
                                              End If
                                              Buffer = String$(Buffer_Size, vbNullChar)
                                              Result = GetPrivateProfileString(Section, Key, vbNullString, Buffer, Buffer_Size, Pathname)
                                          Loop
                                      End If
                                      iniRead = Left$(Buffer, Result)
                                      Exit Function
                                  End If
                              End If
                          End If
                          iniRead = Default
                      End If
                  End If
              End If
          End If
      iniReadError:
      End Function
      

      静态长度缓冲区

      有时我们需要存储具有最大长度或静态长度的变量。用户名、电话号码、颜色代码或 IP 地址是您可能希望限制最大缓冲区长度的字符串示例。必要时这样做可以节省您的时间和精力。

      在下面的代码示例中,Buffer_Max 将被限制为 Buffer_Limit + 1。如果限制大于 64,我们将从 64 开始并像之前一样扩展缓冲区。小于 64,我们只会使用新的缓冲区限制读取一次。

      Private Declare Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Long, ByVal lpFileName As String) As Long
      
      Public Function iniRead(ByVal Pathname As String, ByVal Section As String, ByVal Key As String, Optional ByVal Default As String, Optional Buffer_Limit As Long = 65535) As String
          On Error Resume Next
          If Len(Pathname) <> 0 Then
              Key = Trim$(Key)
              If InStr(1, Key, ";") <> 1 Then
                  Section = Trim$(Section)
                  If Len(Section) > 65535 Then
                      Section = RTrim$(Left$(Section, 65535))
                  End If
                  If InStr(1, Section, "]") = 0 Then
                      If Len(Key) > 65535 Then
                          Key = RTrim$(Left$(Key, 65535))
                      End If
                      If InStr(1, Key, "=") = 0 Then
                          Dim Buffer_Max As Long
                          Err.Clear
                          Buffer_Max = FileLen(Pathname)
                          On Error GoTo iniReadError
                          If Err.Number = 0 Then
                              If Buffer_Max > 4 + Len(Section) + Len(Key) Then
                                  Dim Buffer As String
                                  Dim Result As Long
                                  Dim Buffer_Size As Long
                                  Buffer_Max = Buffer_Max - Len(Section) - Len(Key) - 4
                                  If Buffer_Limit > 65535 Then
                                      Buffer_Limit = 65535
                                  End If
                                  If Buffer_Max > Buffer_Limit Then
                                      Buffer_Max = Buffer_Limit + 1
                                  Else
                                      Buffer_Max = Buffer_Max + 1
                                  End If
                                  If Buffer_Max < 64 Then
                                      Buffer_Size = Buffer_Max
                                  Else
                                      Buffer_Size = 64
                                  End If
                                  Buffer = String$(Buffer_Size, vbNullChar)
                                  Result = GetPrivateProfileString(Section, Key, vbNullString, Buffer, Buffer_Size, Pathname)
                                  If Result <> 0 Then
                                      If Buffer_Max > 64 Then
                                          Do While Result = Buffer_Size - 1 And Buffer_Size < Buffer_Max
                                              Buffer_Size = Buffer_Size * 4
                                              If Buffer_Size > Buffer_Max Then
                                                  Buffer_Size = Buffer_Max
                                              End If
                                              Buffer = String$(Buffer_Size, vbNullChar)
                                              Result = GetPrivateProfileString(Section, Key, vbNullString, Buffer, Buffer_Size, Pathname)
                                          Loop
                                      End If
                                      iniRead = Left$(Buffer, Result)
                                      Exit Function
                                  End If
                              End If
                          End If
                          iniRead = Default
                      End If
                  End If
              End If
          End If
      iniReadError:
      End Function
      

      使用 WritePrivateProfileString

      为确保使用 GetPrivateProfileString 读取字符串不会出现问题,请在使用 WritePrivateProfileString 之前将字符串限制为 65,535 个或更少字符。包含相同的验证也是一个好主意。

      Private Declare Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Long, ByVal lpFileName As String) As Long
      Private Declare Function WritePrivateProfileString Lib "kernel32" Alias "WritePrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpString As Any, ByVal lpFileName As String) As Long
      
      Public Function iniRead(ByVal Pathname As String, ByVal Section As String, ByVal Key As String, Optional ByVal Default As String, Optional Buffer_Limit As Long = 65535) As String
          On Error Resume Next
          If Len(Pathname) <> 0 Then
              Key = Trim$(Key)
              If InStr(1, Key, ";") <> 1 Then
                  Section = Trim$(Section)
                  If Len(Section) > 65535 Then
                      Section = RTrim$(Left$(Section, 65535))
                  End If
                  If InStr(1, Section, "]") = 0 Then
                      If Len(Key) > 65535 Then
                          Key = RTrim$(Left$(Key, 65535))
                      End If
                      If InStr(1, Key, "=") = 0 Then
                          Dim Buffer_Max As Long
                          Err.Clear
                          Buffer_Max = FileLen(Pathname)
                          On Error GoTo iniReadError
                          If Err.Number = 0 Then
                              If Buffer_Max > 4 + Len(Section) + Len(Key) Then
                                  Dim Buffer As String
                                  Dim Result As Long
                                  Dim Buffer_Size As Long
                                  Buffer_Max = Buffer_Max - Len(Section) - Len(Key) - 4
                                  If Buffer_Limit > 65535 Then
                                      Buffer_Limit = 65535
                                  End If
                                  If Buffer_Max > Buffer_Limit Then
                                      Buffer_Max = Buffer_Limit + 1
                                  Else
                                      Buffer_Max = Buffer_Max + 1
                                  End If
                                  If Buffer_Max < 64 Then
                                      Buffer_Size = Buffer_Max
                                  Else
                                      Buffer_Size = 64
                                  End If
                                  Buffer = String$(Buffer_Size, vbNullChar)
                                  Result = GetPrivateProfileString(Section, Key, vbNullString, Buffer, Buffer_Size, Pathname)
                                  If Result <> 0 Then
                                      If Buffer_Max > 64 Then
                                          Do While Result = Buffer_Size - 1 And Buffer_Size < Buffer_Max
                                              Buffer_Size = Buffer_Size * 4
                                              If Buffer_Size > Buffer_Max Then
                                                  Buffer_Size = Buffer_Max
                                              End If
                                              Buffer = String$(Buffer_Size, vbNullChar)
                                              Result = GetPrivateProfileString(Section, Key, vbNullString, Buffer, Buffer_Size, Pathname)
                                          Loop
                                      End If
                                      iniRead = Left$(Buffer, Result)
                                      Exit Function
                                  End If
                              End If
                          End If
                          iniWrite Pathname, Section, Key, Default
                          iniRead = Default
                      End If
                  End If
              End If
          End If
      iniReadError:
      End Function
      
      Public Function iniWrite(ByVal Pathname As String, ByVal Section As String, ByVal Key As String, ByVal Value As String) As Boolean
          On Error GoTo iniWriteError
          If Len(Pathname) <> 0 Then
              Key = Trim$(Key)
              If InStr(1, Key, ";") <> 1 Then
                  Section = Trim$(Section)
                  If Len(Section) > 65535 Then
                      Section = RTrim$(Left$(Section, 65535))
                  End If
                  If InStr(1, Section, "]") = 0 Then
                      If Len(Key) > 65535 Then
                          Key = RTrim$(Left$(Key, 65535))
                      End If
                      If InStr(1, Key, "=") = 0 Then
                          If Len(Value) > 65535 Then Value = Left$(Value, 65535)
                          iniWrite = WritePrivateProfileString(Section, Key, Value, Pathname) <> 0
                      End If
                  End If
              End If
          End If
      iniWriteError:
      End Function
      

      【讨论】:

        【解决方案3】:

        不,很遗憾,没有更好的方法。您必须提供足够大的缓冲区。如果不够,请重新分配缓冲区。我从here 获取了一个代码 sn-p,并根据您的情况进行了调整:

        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 将始终适合。

        【讨论】:

          【解决方案4】:

          也许,在GetPrivateProfileString 之后立即调用GetLastError 是一种方法。如果缓冲区足够大并且没有其他错误,GetLastError 返回 0。如果缓冲区太小,GetLastError 返回234 (0xEA) ERROR_MORE_DATA

          【讨论】:

          • 不幸的是,INI 函数在完全填满缓冲区时总是返回ERROR_MORE_DATA(即使没有截断数据)。
          【解决方案5】:

          我知道这有点晚了,但我想出了一个很棒的解决方案。如果没有剩余缓冲区空间(返回长度 + 1 = 缓冲区长度),则增大缓冲区并再次获取值。重复该过程,直到有剩余的缓冲区空间。

          【讨论】:

            【解决方案6】:

            最好的解决方案肯定是 Brogan 的,但是检查文件大小作为缓冲区大小的上限是错误的。尤其是在处理位于 Windows 或系统文件夹中的 INI 文件时,许多映射到注册表中的键被读取和/或写入。 映射结构见:

            HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\IniFileMapping
            

            有关其工作原理的完整说明,您可以阅读Remarks section of the GetPrivateProfileString documentation

            因此,您可以将许多字符串重新映射到足够长的注册表,但磁盘上的 INI 文件很小。在这种情况下,解决方案无法读取。

            当不使用所需文件的绝对路径或它不在程序的当前工作目录中时,该解决方案还有另一个小问题,因为GetPrivateProfileStrings 在 Windows 目录中搜索初始化文件。此操作不在FileLen 函数中,解决方案也没有对此进行检查。

            最终分配了 64K 的内存并接受了这个限制。

            【讨论】: