The .INI files have a text-based file format for representing application configuration data in a format which is easily editable by humans and readable by a simple automatic parser.
Most people who use Windows have at least a passing acquaintance with .INI files and with the WIN.INI and SYSTEM.INI files in particular (check your Windows directory). Windows stored (and still does) such important information as configuration information about Windows in plain text files that are easily deleted, modified or viewed.
Even though, in 32-bit Windows Microsoft recommends using Registry to store application specific configuration data, in many cases you'll find that using INI files is much faster and safer.
One simple use of INI files, as a status saving mechanism, would be to save the size and location of a form if you want a form to reappear at it's previous position. In general everything you store in Registry you could store in an INI file.
.INI file format
Initialization or Configuration Settings file (.INI) is a text file with 64Kb limit divided into sections, each containing zero or more keys. Each key contains zero or more values. Example:
[SectionName]
keyname=value
;comment
keyname=value
Section names are enclosed in square brackets, and must begin at the beginning of a line. Section and key names are case-insensitive, and cannot contain spacing characters. The key name is followed by an equal sign ("="), optionally surrounded by spacing characters, which are ignored.
If the same section appears more than once in the same file, or if the same key appears more than once in the same section, then the last occurrence prevails.
A key can contain string, integer or boolean value.
Delphi uses INI file format in many cases. For example, the .DSK files (desktop settings) have a file format as described above.
TIniFile
Delphi provides the TIniFile class with methods designed to store and retrieve application-specific information and settings from INI files. The TIniFile class is declared in the inifiles.pas unit. Prior to working with the TIniFile control you need to create an instance of the class:
uses inifiles;
...
var
IniFile : TIniFile;
begin
IniFile := TIniFile.Create('myapp.ini');
|
This code creates an IniFile object and assigns 'myapp.ini' to the only property of the class - the FileName property - used to specify the name of the INI file you are to use.
The code as written above looks (stores) for the myapp.ini file in the \Windows directory. A better way to store application data is in the application's folder - just specify the full path name of the file for the Create method:
// place the INI in the application folder,
// let it have the application name
// and 'ini' for extension:
IniFile := TIniFile.Create(ChangeFileExt(
Application.ExeName,'.ini'));
|
Reading
The TIniFile class has several "read" methods. The ReadString reads a string value from a key, ReadInteger, ReadFloat and similiar are used to read a number from a key. All "read" methods have a default value that can be used if the entry does not exist. For example the ReadString is declared as: function ReadString(const Section, Ident, Default: String): String; override;
Writing
The TIniFile has a corresponding "write" method for each "read" method. In other words they are WriteString, WriteBool, WriteInteger, etc.
| project1.ini |
[User]
Last=Zarko Gajic
Date=12/04/2001
[Placement]
Top=20
Left=35
Width=500
Height=340
|
For example, if we want a program to remember the name of the last person who used it, when it was, and what were the main form coordinates, we might establish a section called "Users" and a keyword called "Last" and "Date" to track the information; and another section called "Placement" with keys "Top", "Left", "Width", "Height".
Note that the key named "Last" holds a string value, "Date" holds a TDateTime value, all keys in the "Placement" section hold an integer value.
The OnCreate event of the main form is the perfect place to store the code needed to access the values in the application's initialization file:
procedure TForm1.FormCreate(Sender: TObject);
var
IniFile : TIniFile;
LastUser : string;
LastDate : TDateTime;
begin
IniFile := TIniFile.Create(
ChangeFileExt(Application.ExeName,'.ini'));
//if no last user return an empty string
LastUser := IniFile.ReadString('User','Last','');
//if no last date return todays date
LastDate := IniFile.ReadDate('User', 'Date', Date);
//show the message
ShowMessage('This program was previously used by '
+ LastUser + ' on '
+ DateToStr(LastDate));
Form1.Top := IniFile.ReadInteger
('Placement','Top', Form1.Top);
Form1.Left := IniFile.ReadInteger
('Placement','Left', Form1.Left);
Form1.Width := IniFile.ReadInteger
('Placement','Width', Form1.Width);
Form1.Height := IniFile.ReadInteger
('Placement','Height', Form1.Height);
IniFile.Free;
end;
|
The OnClose event is ideal for the Save part of the project. Before the application terminates (OnClose of the main form) we save its current position and the info on the user:
procedure TForm1.FormClose
(Sender: TObject; var Action: TCloseAction);
var
IniFile : TIniFile;
begin
IniFile := TIniFile.Create(
ChangeFileExt(Application.ExeName,'.ini'));
IniFile.WriteString('User','Last','Zarko Gajic');
IniFile.WriteDate('User', 'Date', Date);
With IniFile, Form1 do
begin
WriteInteger('Placement','Top', Top);
WriteInteger('Placement','Left', Left);
WriteInteger('Placement','Width', Width);
WriteInteger('Placement','Height', Height);
end;
IniFile.Free;
end;
|
Working with Sections
There are several methods designed to operate on entire sections of the INI file. For example the EraseSection erases an entire section of an INI file. The ReadSection and ReadSections fill a TStringList object with the names of all sections (and key names) in the INI file.
Note: another class in the registry unit, the TRegIniFile class was introduced for easy access to the system's Registry without having to know the structure of the Registry. TRegIniFile uses similar functional access to the Registry as TIniFile does to an INI file.
Limitations and downsides
The TIniFile class uses the Windows API which imposes a limit of 64KB on INI files. If you need to store more than 64KB of data, you should use the TMemIniFile instead, it does not have a limit of 64KB.
Another problem might rise if you have a section with more than 8K value. The ReadSection method uses the GetPrivateProfileString API function with the 8K buffer. One way to solve the problem is to write your own version of the ReadSection method.
The fact that anyone could access and change INI files means that the integrity of the information contained in the files could not be guaranteed. Yet, the Registry too can be edited with the tools provided by Microsoft.
That's it. I hope you'll think twice the next time you ask yourself "where should I store my application's data". Anyway if you have any questions or comments to this article, feel free to post them on the Delphi Programming Forum.