Articles   Members Online:
-Article/Tip Search
-News Group Search over 21 Million news group articles.
Member Area
-Account Center
-Top 10 NEW!!
-Submit Article/Tip
-Forums Upgraded!!
-My Articles
-Edit Information
-Become a Member
-Why sign up!
-Chat Online!
-Indexes NEW!!
-Build your resume
-Find a job
-Post a job
-Resume Search
-Link to us
Visit Embarcadero
Embarcadero Community
Hidden limitations in TIniFile Turn on/off line numbers in source code. Switch to Orginial background IDE or DSP color Comment or reply to this aritlce/tip for discussion. Bookmark this article to my favorite article(s). Print this article
Delphi All Versions
User Rating
No Votes
# Votes
DSP, Administrator
Reference URL:
			Author: Lou Adler 

I have an INI file that is approximately 20K with all entries in one section. If I 
use TIniFile's ReadSection method, only part of the section gets loaded. Why?


A reader asked me this question a few days ago, and I must admit that it stumped me 
at first. He was trying to load an INI file's section that had several lines in it 
(amounting to over 16K of text) that he needed to load into a combo box. The 
section contained listings of several modem makes and models that he was going to 
use in his application so users could pick the modems that were on their machines.

To approach his problem, he created a TIniFile object and used the ReadSection 
method to read the section containing the list of modems into a TStrings object, 
which happened to be the Items property of a TComboBox. His code worked fine with 
one exception: ReadSection got about a third of the way through the list, then 
mysteriously stopped loading values, and truncated in the middle of a line! 
Intrigued, I decided to look into it, and much to my surprise, found a very 
interesting quirk in the code for ReadSection in the IniFiles.pas source file.

An Undocumented Limitation

The first stop in my investigation had me testing some code out in loading a huge 
section of an INI file into a ComboBox. I used the following procedure adapted from 
a snippet my reader sent to me:
2   procedure ComboLoadIniSection(IniFileName, SectionName: string; const List 
3   TStrings);
4   var
5     ini: TIniFile;
6   begin
7     list.clear;
8     if FileExists(IniFileName) then
9       ini := TIniFile.Create(IniFileName);
10    with ini do
11    try
12      ReadSection(SectionName, list);
13    finally
14      Free;
15    end;
16  end;

The code above looks pretty straightforward. In fact it works incredibly well, with 
absolutely no errors. I used it on some fairly generic INI files with just a few 
lines of key values first, and the Items property of my ComboBox was loaded just 
fine. It was when I used the sample file the reader sent containing the modem 
listings that things went awry. The procedure still executed fine with no errors, 
but truncated about a third of the way through the list. It looked like I was going 
to have to look into the source file.

Here's the listing for the ReadSection code in the IniFiles.Pas VCL Source file:
18  procedure TIniFile.ReadSection(const Section: string; Strings: TStrings);
19  const
20    BufSize = 8192;
21  var
22    Buffer, P: PChar;
23  begin
24    GetMem(Buffer, BufSize);
25    try
26      Strings.BeginUpdate;
27      try
28        Strings.Clear;
29        if GetPrivateProfileString(PChar(Section), nil, nil, Buffer, BufSize,
30          PChar(FFileName)) <> 0 then
31        begin
32          P := Buffer;
33          while P^ <> #0 do
34          begin
35            Strings.Add(P);
36            Inc(P, StrLen(P) + 1);
37          end;
38        end;
39      finally
40        Strings.EndUpdate;
41      end;
42    finally
43      FreeMem(Buffer, BufSize);
44    end;
45  end;

Looks like your basic WinAPI wrapper function. But there's one strange thing about 
it, and it has to do with the call to GetPrivateProfileString. This is a 
WinAPI-level call that is used to read a specific section of an INI file and loads 
one or all of its key values into a buffer. The buffer has the following structure: 
keyValue#0keyValue#0keyValue#0keyValue#0#0 where keyValue is a specific key value 
in a section. The WinAPI help file states that if the size of the strings in the 
section exceed the allocated buffer size, the buffer is truncated to the allocated 
size and two nulls are appended to the end of the string.

So going back to the code listing above, what do you see? Right! The buffer size is 
only 8K! So any section that has more than 8K in it will be truncated. That's why 
only part of the list was added into the ComboBox at runtime. I'm sure there was a 
good reason for the developer who wrote this wrapper to do this  probably to save 
memory space and go on the assumption that no one would ever need to have anything 
larger than 8K to read. But for those that do need to load in more than 8K, this is 
a serious limitation.

So how do you work around this? Well, at first thought, I figured upon creating a 
new descendant class off of TIniFile. But I checked myself because all the methods 
of TIniFile are static, so in order to do an override of a method, I'd have to 
write it over completely. Not a big deal, but then I'd have to deal with the 
overhead of adding the component into the VCL (and if you're like me, you've got a 
lot of components installed on your pallette). In the end, I decided to copy the 
source code and make a generic utility routine that I put in a library that I use 
for all my programs. Here's the code:
47  procedure INISectLoadList(IniFileName, SectionName: PChar; const list: TStrings);
48  const
49    BufSize = 32768; //Changed from 8192
50  var
51    Buffer, P: PChar;
52  begin
53    GetMem(Buffer, BufSize);
54    try
55      list.BeginUpdate;
56      try
57        list.Clear;
58        if GetPrivateProfileString(SectionName, nil, nil, Buffer, BufSize,
59          IniFileName) <> 0 then
60        begin
61          P := Buffer;
62          while P^ <> #0 do
63          begin
64            List.Add(P);
65            Inc(P, StrLen(P) + 1);
66          end;
67        end;
68      finally
69        List.EndUpdate;
70      end;
71    finally
72      FreeMem(Buffer, BufSize);
73    end;
74  end;

This is essentially a replica of the code above, with one exception: It now has a 
32K buffer size. If you look up the GetPrivateProfileString in the help system, 
you'll see that the function is in the API code for backward compatibility with 
16-bit applications. And as you may know, there is a 32K resource limit with 16- 
bit apps. Thus, your buffer can't be bigger than this. But this should be plenty of 
space to work with for 99 percent of the applications out there. However, for those 
of you making the move to Win95 and NT, the registry is where you should put 
runtime parameters.

Stay tuned for an article on the registry coming up. I'm still doing the research on it.

Vote: How useful do you find this Article/Tip?
Bad Excellent
1 2 3 4 5 6 7 8 9 10


Share this page
Download from Google

Copyright © Mendozi Enterprises LLC