Files Delphi

Title: personal settings and ini-files
Question: This article illustrates the usage of the TInifile object, and gives guideline when and how to use ini files for the storage of personal settings.
Answer:
INI
INI-files.
This article is part of a series of five articles about preserving user
sensitive settings.
INI-files are meant to retain settings between instances of your
applications. Their structure is very simple, which limits their functionality.
This article explains the structure of ini files, and the basics of how to read and write
them from Delphi.
Structure of ini files
Lets first have a look at the structure of ini-files. Basically, an ini file
has a number of blocks, enclosed in square brackets, and every block has some
settings.
Example: here is a part of my W2000 win.ini file:
[WinZip]
version=6.1-6.2
Note-1=This section is required only to install the optional WinZip Internet Browser Support build 0231.
Note-2=Removing this section of the win.ini will have no effect except preventing installation of WinZip Internet Browser Support build 0231.
win32_version=R6.3-7.0
[Solitaire]
Options=91
[MSUCE]
Advanced=0
CodePage=Unicode
Font=Arial
[MAPI 1.0 Time Zone]
Bias=0
StandardName=GMT Standard Time
StandardBias=0
StandardStart=00000A00050002000000000000000000
DaylightName=GMT Daylight Time
DaylightBias=ffffffc4
DaylightStart=00000300050001000000000000000000
ActiveTimeBias=0
We have blocks between square brackets, such as [WINZIP], [Solitaire], and [MAPI 1.0 Time Zone].
Winzip has 4 data items, version, Note-1, Note-2 and win32_version.
The data behind an item name can be alphabetical, numeric or boolean. Binary
data is limited to those data which does no contains a #0 or CR/LF. Blank
lines may be used between the blocks.
As you can see above, many applications use the win.ini file to store
settings-information. You are free to choose the win.ini file. If you have just
a few settings, this may be the right choice. Other applications should not
suffer in any way. If you have more than one block of information, it is
preferable to define your own ini file.
Writing ini-files from Delphi
Delphi provides us with a TIniFile object. This object is defined in the unit
ini-files. Add this unit to your uses clause. Then create :
lIniFileVar := TIniFile.create(FileName);
You may or may not include a path with the filename. If you don't, windows
will assume it must be created in the windows directory. This is the default. By
using the ExtractFileDir(Application.Exename), you can easily create ini-files
in the directory in which your application is created. Simply pass the entire
path with the file name.
If the file already exists, windows will open it. If it does not, windows
will create it.
The next thing you will want is to write some information to it. We will
construct a small demo application. Start your Delphi, choose new application,
and save your form as formDemoIniFile, and your project as DemoIniFile. Put a
textbox, a SaveFile dialog and an OpenDialog component on your form. Next, drop
two buttons on your form, and call them btnExit and btnOpenFile.
In the btnOpenFileClick event, write:


procedure TForm1.btnFileOpenClick(Sender: TObject);
var
lIniFileVar : TIniFile;
begin
OpenDialog1.Filter := 'Text files |*.txt|All files|*.*';

if OpenDialog1.execute then
begin
edit1.text := OpenDialog1.FileName;
lIniFileVar := TIniFile.create('DemoApp.ini');
lIniFileVar.WriteString('OPENEDFILES', 'OPENDIALOG1', edit1.text);
lIniFileVar.WriteString('OPENEDFILES', 'OPENDIALOG1LASTDIR', ExtractFileDir(edit1.text));
lIniFileVar.free;

end;

end;




lIniFileVar is a local variable in this routine of th type TIniFile. When we
create it, we pass the filename, in this case DemoApp.ini. Next we use the
WriteString method to write the contents of to the edit1.text to the inifile. We
specify this string must be stored in the block OPENEDFILES and that the item
name = OPENDIALOG1. next we also write the directory.
After we have run this program, the result might look:
[OPENEDFILES]
OPENDIALOG1=E:\program files\delforex\License.txt
OPENDIALOG1LASTDIR=E:\program files\delforex
Writing numeric data is essentially the same, and so is writing booleans.
Reading them from Delphi.
Of course we gain nothing when we can write data but cann't read them. So we
expand our example a bit with a few lines to read the previous data before we
present the OpenFileDialog.


procedure TForm1.btnFileOpenClick(Sender: TObject);
var
lIniFileVar : TIniFile;
begin
// read old data and assign them to OpenFile dialog.
lIniFileVar := TIniFile.create('DemoApp.ini');
OpenDialog1.FileName := lIniFileVar.ReadString('OPENEDFILES', 'OPENDIALOG1', '');
OpenDialog1.InitialDir :=lIniFileVar.ReadString('OPENEDFILES', 'OPENDIALOG1LASTDIR', '');
lIniFileVar.Free;
OpenDialog1.Filter := 'Text files |*.txt|All files|*.*';
// ask user to open file
if OpenDialog1.execute then
begin
edit1.text := OpenDialog1.FileName;
// Store new file data in ini file.
lIniFileVar := TIniFile.create('DemoApp.ini');
lIniFileVar.WriteString('OPENEDFILES', 'OPENDIALOG1', edit1.text);
lIniFileVar.WriteString('OPENEDFILES', 'OPENDIALOG1LASTDIR', ExtractFileDir(edit1.text));
lIniFileVar.free;
end;
end;


Note that the ReadString Function requires a third argument, this is the
default value. Note that one may use the ReadSectionValues (const Section: string; Strings:
TStrings) method to read all values of an entire section.
Hacking delphi
There are some circumstances in which you might want to read an entire block
(also called 'section'). If you wish to use this function, some Delphi hacking
might be useful. By default, the buffer for the reading sections
is 16K. You can upgrade this to 32K no problem.
Simply start Delphi, open \Program Files\Borland\Delphi5\Source\Vcl\inifil.pas,
and look for the ReadSection and ReadSections procedures. Both have a
constant :
BufSize = 16384;
Change this constant to 32768 and you claim double the amount of memory.
When you study this unit, you will find that all methods boil down to usage
of the windows WritePrivateProfileString and GetPrivateProfileString functions.
The unit has no WriteSectionValues procedure. Should you wish, it can be
easily added.


procedure TCustomIniFile.WriteSectionValues(const Section:
string; Strings: TStrings);
var
KeyList: TStringList;
i: Integer;
begin
KeyList := TStringList.Create;
for i := 0 To Strings.Count-1 do
begin
WriteString(Section, Strings.Names[i],
Strings.Values[Strings.Names[i]]);
end;
end;



Alternative
There is an alternative for the usage of the TInifile object. Any TStringList
has a LoadFromFile and SaveToFile method. Using the Values property, one could
extract item values from them, and even change them. But as these methods do not
adhere to the windows api's and their rules about file locations, this practice
is not recommended. Also, as the Values property does not support usage of
sections, this may lead to problems with duplicate item names.
Conclusion
You now know how to use ini-files. You should also be aware of its
possibilities. As for its limitations: Don't try to store binary data. Neither
store strings which contain a CR/LF, as your values can be just 1 line of
length..