CIniFile: Difference between revisions

From DataFlex Wiki
Jump to navigationJump to search
m doh! - fixed cat tutorial
m Sets _not_ Gets!
 
(4 intermediate revisions by 2 users not shown)
Line 1: Line 1:
An object of the cIniFile class can be used for reading information avaiable from an INI file.
An object of the '''cIniFile''' class can be used for reading information avaiable from an INI file.


INI files are often used to store user preferences or setup options for applications.
INI files are often used to store user preferences or setup options for applications.


==INI files==
In [[Visual DataFlex]] you can freely use as many INI files as you require, however the two-level structure of INI files (sections and keys) means that there is often no need to use more than one.  Here, for example, is a boot.ini file (usually found in the root directory of the system drive of Windows operating systems):
In [[Visual DataFlex]] you can freely use as many INI files as you require, however the two-level structure of INI files (sections and keys) means that there is often no need to use more than one.  Here, for example, is a boot.ini file (usually found in the root directory of the system drive of Windows operating systems):


[boot loader]
<source lang="ini">
timeout=30
[boot loader]
default=multi(0)disk(0)rdisk(0)partition(2)\WINDOWS
timeout=30
default=multi(0)disk(0)rdisk(0)partition(2)\WINDOWS
[operating systems]
 
multi(0)disk(0)rdisk(0)partition(2)\WINDOWS="Microsoft Windows XP Professional" /fastdetect /NoExecute=OptIn
[operating systems]
multi(0)disk(0)rdisk(0)partition(2)\WINDOWS="Microsoft Windows XP Professional" /fastdetect /NoExecute=OptIn
</source>


As you can see, it defines two sections - [boot loader] and [operating systems] - each with different settings within them.
As you can see, it defines two sections - [boot loader] and [operating systems] - each with different settings within them.


Another example are the workspace configuration files used by [[Visual DataFlex]] itself (by default named "Config.ws", showing that they don't have to use the .ini extension - see the artice in "See Also" below), which must exist in the same directory the program is run from (usually the "Programs" subdirectory of the workspace).  Here is an example:
==Visual DataFlex Workspace files==
Another example are the workspace configuration files used by [[Visual DataFlex]] itself (by default named "Config.ws", showing that they don't have to use the .ini extension - see the article in "See Also" below), which must exist in the same directory the program is run from (usually the "Programs" subdirectory of the workspace).  Here is an example:


[Workspace]
<source lang="ini">
Home=..\
[Workspace]
AppSrcPath=.\AppSrc
Home=..\
AppHTMLPath=.\AppHtml
AppSrcPath=.\AppSrc
BitmapPath=.\Bitmaps
AppHTMLPath=.\AppHtml
IdeSrcPath=.\IdeSrc
BitmapPath=.\Bitmaps
DataPath=.\Data
IdeSrcPath=.\IdeSrc
DDSrcPath=.\DdSrc
DataPath=.\Data
HelpPath=.\Help
DDSrcPath=.\DdSrc
ProgramPath=.\Programs
HelpPath=.\Help
FileList=.\Data\Filelist.cfg
ProgramPath=.\Programs
Description=MyFirstProject
FileList=.\Data\Filelist.cfg
Description=MyFirstProject
</source>


Here only a single section - [Workspace] - is defined, however the fact that an INI file can have as many sections as it needs means that you can use this very file to set up options for how you want your program to behave.
Here only a single section - [Workspace] - is defined, however the fact that an INI file can have as many sections as it needs means that you can use this very file to set up options for how you want your program to behave.
Line 33: Line 39:
Note that section names (the ones in the square brackets), key names ("Home", "AppSrcPath", etc.) and key values can all contain spaces (although for my own part I prefer not to use those and depend on [http://en.wikipedia.org/wiki/Camelcase camelCase] to supply readability).
Note that section names (the ones in the square brackets), key names ("Home", "AppSrcPath", etc.) and key values can all contain spaces (although for my own part I prefer not to use those and depend on [http://en.wikipedia.org/wiki/Camelcase camelCase] to supply readability).


==INI file advantages==
The advantage of the INI file approach is that it can make programs configurable for different environments or uses without them having to be recompiled with different settings each time: the program can be made with a set of default values which can then be overridden at run-time by the settings in an INI file.  It has the advantage over Registry settings, which perform very much the same function, in that you don't need any special rights on the machine to set it up and make changes to it: you are just working with text files, so security policy issues about who has rights to the [http://en.wikipedia.org/wiki/Windows_Registry Windows Registry], or who can run RegEdit or RegEdit32 do not apply - if you can run Notepad and modify files you can configure your application.
The advantage of the INI file approach is that it can make programs configurable for different environments or uses without them having to be recompiled with different settings each time: the program can be made with a set of default values which can then be overridden at run-time by the settings in an INI file.  It has the advantage over Registry settings, which perform very much the same function, in that you don't need any special rights on the machine to set it up and make changes to it: you are just working with text files, so security policy issues about who has rights to the [http://en.wikipedia.org/wiki/Windows_Registry Windows Registry], or who can run RegEdit or RegEdit32 do not apply - if you can run Notepad and modify files you can configure your application.


Here is an example where an additional section allowing the configuration of a database location and login has been added:
Here is an example where an additional section allowing the configuration of a database location and login has been added:


[Workspace]
<source lang="ini">
Home=..\
[Workspace]
AppSrcPath=.\AppSrc
Home=..\
AppHTMLPath=.\AppHtml
AppSrcPath=.\AppSrc
BitmapPath=.\Bitmaps
AppHTMLPath=.\AppHtml
IdeSrcPath=.\IdeSrc
BitmapPath=.\Bitmaps
DataPath=.\Data
IdeSrcPath=.\IdeSrc
DDSrcPath=.\DdSrc
DataPath=.\Data
HelpPath=.\Help
DDSrcPath=.\DdSrc
ProgramPath=.\Programs
HelpPath=.\Help
FileList=.\Data\Filelist.cfg
ProgramPath=.\Programs
Description=MyFirstProject
FileList=.\Data\Filelist.cfg
Description=MyFirstProject
   
   
[Database Login]
[Database Login]
Server=big-iron.database-servers.local
Server=big-iron.database-servers.local
Login=itsme
Login=itsme
Password=letmein
Password=letmein
Database=Accounting
Database=Accounting
Driver=MSSQLDRV
Driver=MSSQLDRV
 
</source>
Combining these factors - the fact that there is probably no need to have more than one INI file, and the fact that [[Visual DataFlex]] requres one anyway - means that for most purposes you can just add what you need to the existing workspace configuration file.
Combining these factors - the fact that there is probably no need to have more than one INI file, and the fact that [[Visual DataFlex]] requres one anyway - means that for most purposes you can just add what you need to the existing workspace configuration file.


==Accessing INI files==
So how do you access that information in your program?
So how do you access that information in your program?


Line 67: Line 76:
Here is one way of doing it ('''''note''' - this code is assumed to be in the oApplication object or a sub-class of the cApplication class; the only place this matters is in the "Set psFileName ..." line, where "Self" will refer to the oApplication object, and where the various properties are held''):
Here is one way of doing it ('''''note''' - this code is assumed to be in the oApplication object or a sub-class of the cApplication class; the only place this matters is in the "Set psFileName ..." line, where "Self" will refer to the oApplication object, and where the various properties are held''):


  String  sServer sLogin sPW sDrv sDB
<source lang="vdf">
  Boolean bLogin
String  sServer sLogin sPW sDrv sDB
  Handle  hoIni
Boolean bLogin
Handle  hoIni
   
   
  // Retrieve your application defaults into local variables
// Retrieve your application defaults into local variables
  Get psServer  to sServer
Get psServer  to sServer
  Get psLogin    to sLogin
Get psLogin    to sLogin
  Get psPassword to sPW
Get psPassword to sPW
  Get psDriver  to sDrv
Get psDriver  to sDrv
  Get psDatabase to sDB
Get psDatabase to sDB
  Get pbDoLogin  to bDB
Get pbDoLogin  to bDB
 
  Get Create U_cIniFile to hoIni    // Create INI file object
Get Create U_cIniFile to hoIni    // Create INI file object
 
  // Make it use the workspace configuration file
// Make it use the workspace configuration file
  Set psFileName of hoIni to (psWorkspaceWSFile(phoWorkspace(Self)))
Set psFileName of hoIni to (psWorkspaceWSFile(phoWorkspace(Self)))
 
  // Read the information
// Read the information
  Move (ReadString(hoIni, "Database Login", "Server",  sServer)) to sServer
Move (ReadString(hoIni, "Database Login", "Server",  sServer)) to sServer
  Move (ReadString(hoIni, "Database Login", "Login",    sLogin))  to sLogin
Move (ReadString(hoIni, "Database Login", "Login",    sLogin))  to sLogin
  Move (ReadString(hoIni, "Database Login", "Password", sPW))    to sPW
Move (ReadString(hoIni, "Database Login", "Password", sPW))    to sPW
  Move (ReadString(hoIni, "Database Login", "Driver",  sDrv))    to sDrv
Move (ReadString(hoIni, "Database Login", "Driver",  sDrv))    to sDrv
  Move (ReadString(hoIni, "Database Login", "Database", sDB))    to sDB
Move (ReadString(hoIni, "Database Login", "Database", sDB))    to sDB
  Move (ReadString(hoIni, "Database Login", "DoLogin",  bLogin))  to bLogin
Move (ReadString(hoIni, "Database Login", "DoLogin",  bLogin))  to bLogin
     
 
  Send Destroy of hoIni            // And Destroy it again
Send Destroy of hoIni            // And Destroy it again
 
  // Set the properties to make the information persistent
// Set the properties to make the information persistent
  Get psServer  to sServer
Set psServer  to sServer
  Get psLogin    to sLogin
Set psLogin    to sLogin
  Get psPassword to sPW
Set psPassword to sPW
  Get psDriver  to sDrv
Set psDriver  to sDrv
  Get psDatabase to sDB
Set psDatabase to sDB
  Get pbDoLogin  to bDB
Set pbDoLogin  to bDB
</source>


The above code will retrieve the values set in the second workspace file example above.  Note that in that example, the "DoLogin" value is not present - the "''ReadString''" method of the cIniFile class lets you supply a default value to use if the setting is not present, and here we have used the bLogin variable, which was set from the pbDoLogin property, and which is then used to set that property again... so the fact that the setting is absent (not "''missing''" - it is deliberate!) means that the default property remains unchanged.
The above code will retrieve the values set in the second workspace file example above.  Note that in that example, the "DoLogin" value is not present - the "''ReadString''" method of the cIniFile class lets you supply a default value to use if the setting is not present, and here we have used the bLogin variable, which was set from the pbDoLogin property, and which is then used to set that property again... so the fact that the setting is absent (not "''missing''" - it is deliberate!) means that the default property remains unchanged.
Line 106: Line 117:
You could save some code by not retrieving the properties into local variables first, but using the properties as defaults directly:
You could save some code by not retrieving the properties into local variables first, but using the properties as defaults directly:


  Move (ReadString(hoIni, "Database Login", "Server",  psServer(Self)))  to sServer
<source lang="dataflex">
  Move (ReadString(hoIni, "Database Login", "Login",    psLogin(Self)))    to sLogin
Move (ReadString(hoIni, "Database Login", "Server",  psServer(Self)))  to sServer
  Move (ReadString(hoIni, "Database Login", "Password", psPassword(Self))) to sPW
Move (ReadString(hoIni, "Database Login", "Login",    psLogin(Self)))    to sLogin
  Move (ReadString(hoIni, "Database Login", "Driver",  psDriver(Self)))  to sDrv
Move (ReadString(hoIni, "Database Login", "Password", psPassword(Self))) to sPW
  Move (ReadString(hoIni, "Database Login", "Database", psDatabase(Self))) to sDB
Move (ReadString(hoIni, "Database Login", "Driver",  psDriver(Self)))  to sDrv
  Move (ReadString(hoIni, "Database Login", "DoLogin",  pbDoLogin(Self)))  to bLogin
Move (ReadString(hoIni, "Database Login", "Database", psDatabase(Self))) to sDB
Move (ReadString(hoIni, "Database Login", "DoLogin",  pbDoLogin(Self)))  to bLogin
</source>


Or even dispense with the local variables completely:
Or even dispense with the local variables completely:


  Set psServer  to (ReadString(hoIni, "Database Login", "Server",  psServer(Self)))
<source lang="dataflex">
  Set psLogin    to (ReadString(hoIni, "Database Login", "Login",    psLogin(Self)))
Set psServer  to (ReadString(hoIni, "Database Login", "Server",  psServer(Self)))
  Set psPassword to (ReadString(hoIni, "Database Login", "Password", psPassword(Self)))
Set psLogin    to (ReadString(hoIni, "Database Login", "Login",    psLogin(Self)))
  Set psDriver  to (ReadString(hoIni, "Database Login", "Driver",  psDriver(Self)))
Set psPassword to (ReadString(hoIni, "Database Login", "Password", psPassword(Self)))
  Set psDatabase to (ReadString(hoIni, "Database Login", "Database", psDatabase(Self)))
Set psDriver  to (ReadString(hoIni, "Database Login", "Driver",  psDriver(Self)))
  Set pbDoLogin  to (ReadString(hoIni, "Database Login", "DoLogin",  pbDoLogin(Self)))
Set psDatabase to (ReadString(hoIni, "Database Login", "Database", psDatabase(Self)))
Set pbDoLogin  to (ReadString(hoIni, "Database Login", "DoLogin",  pbDoLogin(Self)))
</source>


But some might consider that a bridge too far!
But some might consider that a bridge too far!


==Summary==
The cIniFile has other methods as well: it can write as well as read (WriteString, DeleteKey and DeleteSection) and can dynamically determine the contents of the file (ReadSections, ReadSection, SectionExists and KeyExists) - see the VDF Help for full information - but the ReadString method is the one you will need in most circumstances.
The cIniFile has other methods as well: it can write as well as read (WriteString, DeleteKey and DeleteSection) and can dynamically determine the contents of the file (ReadSections, ReadSection, SectionExists and KeyExists) - see the VDF Help for full information - but the ReadString method is the one you will need in most circumstances.



Latest revision as of 09:01, 11 August 2021

An object of the cIniFile class can be used for reading information avaiable from an INI file.

INI files are often used to store user preferences or setup options for applications.

INI files

In Visual DataFlex you can freely use as many INI files as you require, however the two-level structure of INI files (sections and keys) means that there is often no need to use more than one. Here, for example, is a boot.ini file (usually found in the root directory of the system drive of Windows operating systems):

[boot loader]
timeout=30
default=multi(0)disk(0)rdisk(0)partition(2)\WINDOWS

[operating systems]
multi(0)disk(0)rdisk(0)partition(2)\WINDOWS="Microsoft Windows XP Professional" /fastdetect /NoExecute=OptIn

As you can see, it defines two sections - [boot loader] and [operating systems] - each with different settings within them.

Visual DataFlex Workspace files

Another example are the workspace configuration files used by Visual DataFlex itself (by default named "Config.ws", showing that they don't have to use the .ini extension - see the article in "See Also" below), which must exist in the same directory the program is run from (usually the "Programs" subdirectory of the workspace). Here is an example:

[Workspace]
Home=..\
AppSrcPath=.\AppSrc
AppHTMLPath=.\AppHtml
BitmapPath=.\Bitmaps
IdeSrcPath=.\IdeSrc
DataPath=.\Data
DDSrcPath=.\DdSrc
HelpPath=.\Help
ProgramPath=.\Programs
FileList=.\Data\Filelist.cfg
Description=MyFirstProject

Here only a single section - [Workspace] - is defined, however the fact that an INI file can have as many sections as it needs means that you can use this very file to set up options for how you want your program to behave.

Note that section names (the ones in the square brackets), key names ("Home", "AppSrcPath", etc.) and key values can all contain spaces (although for my own part I prefer not to use those and depend on camelCase to supply readability).

INI file advantages

The advantage of the INI file approach is that it can make programs configurable for different environments or uses without them having to be recompiled with different settings each time: the program can be made with a set of default values which can then be overridden at run-time by the settings in an INI file. It has the advantage over Registry settings, which perform very much the same function, in that you don't need any special rights on the machine to set it up and make changes to it: you are just working with text files, so security policy issues about who has rights to the Windows Registry, or who can run RegEdit or RegEdit32 do not apply - if you can run Notepad and modify files you can configure your application.

Here is an example where an additional section allowing the configuration of a database location and login has been added:

[Workspace]
Home=..\
AppSrcPath=.\AppSrc
AppHTMLPath=.\AppHtml
BitmapPath=.\Bitmaps
IdeSrcPath=.\IdeSrc
DataPath=.\Data
DDSrcPath=.\DdSrc
HelpPath=.\Help
ProgramPath=.\Programs
FileList=.\Data\Filelist.cfg
Description=MyFirstProject
 
[Database Login]
Server=big-iron.database-servers.local
Login=itsme
Password=letmein
Database=Accounting
Driver=MSSQLDRV

Combining these factors - the fact that there is probably no need to have more than one INI file, and the fact that Visual DataFlex requres one anyway - means that for most purposes you can just add what you need to the existing workspace configuration file.

Accessing INI files

So how do you access that information in your program?

For most purposes the information in an INI file will only be read at program start-up. This means that (a) the oApplication object is a good place to do such things (see the article in "See Also" below for some guidance on sub-classing the cApplicatiion class) and (b) the cIniFile object need not be retained after it has been used. (Of course you might have different requirements, but here we will assume these are true.)

So... in your oApplication object (perhaps in the "OnCreate" procedure, perhaps elsewhere, such as in "Construct_Object" or "End_Construct_Object") what you need to do is dynamically create a cIniFile object, use it to get the information you want, then destroy it again. You also want to be able to access the workspace configuration file for the application, perhaps without knowing ahead of time what that is actually called.

Here is one way of doing it (note - this code is assumed to be in the oApplication object or a sub-class of the cApplication class; the only place this matters is in the "Set psFileName ..." line, where "Self" will refer to the oApplication object, and where the various properties are held):

String  sServer sLogin sPW sDrv sDB
Boolean bLogin
Handle  hoIni
 
// Retrieve your application defaults into local variables
Get psServer   to sServer
Get psLogin    to sLogin
Get psPassword to sPW
Get psDriver   to sDrv
Get psDatabase to sDB
Get pbDoLogin  to bDB

Get Create U_cIniFile to hoIni    // Create INI file object

// Make it use the workspace configuration file
Set psFileName of hoIni to (psWorkspaceWSFile(phoWorkspace(Self)))

// Read the information
Move (ReadString(hoIni, "Database Login", "Server",   sServer)) to sServer
Move (ReadString(hoIni, "Database Login", "Login",    sLogin))  to sLogin
Move (ReadString(hoIni, "Database Login", "Password", sPW))     to sPW
Move (ReadString(hoIni, "Database Login", "Driver",   sDrv))    to sDrv
Move (ReadString(hoIni, "Database Login", "Database", sDB))     to sDB
Move (ReadString(hoIni, "Database Login", "DoLogin",  bLogin))  to bLogin

Send Destroy of hoIni             // And Destroy it again

// Set the properties to make the information persistent
Set psServer   to sServer
Set psLogin    to sLogin
Set psPassword to sPW
Set psDriver   to sDrv
Set psDatabase to sDB
Set pbDoLogin  to bDB

The above code will retrieve the values set in the second workspace file example above. Note that in that example, the "DoLogin" value is not present - the "ReadString" method of the cIniFile class lets you supply a default value to use if the setting is not present, and here we have used the bLogin variable, which was set from the pbDoLogin property, and which is then used to set that property again... so the fact that the setting is absent (not "missing" - it is deliberate!) means that the default property remains unchanged.

You could save some code by not retrieving the properties into local variables first, but using the properties as defaults directly:

Move (ReadString(hoIni, "Database Login", "Server",   psServer(Self)))   to sServer
Move (ReadString(hoIni, "Database Login", "Login",    psLogin(Self)))    to sLogin
Move (ReadString(hoIni, "Database Login", "Password", psPassword(Self))) to sPW
Move (ReadString(hoIni, "Database Login", "Driver",   psDriver(Self)))   to sDrv
Move (ReadString(hoIni, "Database Login", "Database", psDatabase(Self))) to sDB
Move (ReadString(hoIni, "Database Login", "DoLogin",  pbDoLogin(Self)))  to bLogin

Or even dispense with the local variables completely:

Set psServer   to (ReadString(hoIni, "Database Login", "Server",   psServer(Self)))
Set psLogin    to (ReadString(hoIni, "Database Login", "Login",    psLogin(Self)))
Set psPassword to (ReadString(hoIni, "Database Login", "Password", psPassword(Self)))
Set psDriver   to (ReadString(hoIni, "Database Login", "Driver",   psDriver(Self)))
Set psDatabase to (ReadString(hoIni, "Database Login", "Database", psDatabase(Self)))
Set pbDoLogin  to (ReadString(hoIni, "Database Login", "DoLogin",  pbDoLogin(Self)))

But some might consider that a bridge too far!

Summary

The cIniFile has other methods as well: it can write as well as read (WriteString, DeleteKey and DeleteSection) and can dynamically determine the contents of the file (ReadSections, ReadSection, SectionExists and KeyExists) - see the VDF Help for full information - but the ReadString method is the one you will need in most circumstances.

See Also

Passing the workspace as a parameter

External references