Visual Basic Explorer
Visual Basic Explorer
 Navigation
 Home


 Coding
 Source Code

 FAQ Center

 VB Tips

 Downloads

 ToolBox

 Tutorials

 VB Games

 VB News

 VB Award

 VB Forums



 Affiliates
 Planet Source Code

 Rent a Coder

 DirectX4VB


 Misc
 Search

 Feedback

 Advertise

 About


Need to hire
a VB coder?

Please support our sponsor:

 Home 
 Site Map 
 Forums 
 News 
 Feedback 

Focus On Visual Basic
A special Focus Section of Visual Basic Explorer

Part 2

<< Prev] [Main >>

Examples using the API functions.

The two sets of API functions provide identical functionality.  The only difference being which .INI file is accessed.  For this reason, the examples I give will only use the API functions that access "private" .INI files (e.g., GetPrivateProfileString) and not the ones that access the WIN.INI file exclusively (e.g., GetProfileString).

When describing strings containing NULL characters, I'll use "ø" to represent a NULL character.  For example, the list "AøBøCøø" contains the letters "A", "B" and "C" delimited by NULLs and terminated with two NULLs.

Normally, when pre-sizing a string buffer that will be used as the return value from an API, you would create a string of spaces or NULLs.  However, in the following examples, I will use a string of "*"s so that it will be easier to see how much of the string was actually filled by each of the API calls.

Let's define a few variables and a constant that we'll use throughout the examples:

Const sFile As String = "MyINI.ini"

Dim sString  As String
Dim lSize    As Long
Dim lReturn  As Long

Dim sNull  As String
sNull = Chr$(0)

As we progress through the examples, the API calls will access and modify the MyINI.ini file.  I'll show what MyINI.ini should look like after each example.


This first write will create the .INI file, the section and the entry.
lReturn = WritePrivateProfileString("Options", "Path", "C:\Games", sFile)

Contents of MyINI.ini
[Options]
Path=C:\Games


Replace the Options section.
sString = "Sound=" & CStr(True) & sNull _
        & "Skill=Novice" & sNull & sNull
lReturn = WritePrivateProfileSection("Options", sString, sFile)

Contents of MyINI.ini
[Options]
Sound=True
Skill=Novice


Add a section to hold player #1's statistics.
sString = "Name=Brian" & sNull _
        & "Score=" & CStr(3500) & sNull _
        & "Level=" & CStr(2) & sNull & sNull
lReturn = WritePrivateProfileSection("Player #1", sString, sFile)

Contents of MyINI.ini
[Options]
Sound=True
Skill=Novice

[Player #1]
Name=Brian
Score=3500
Level=2


Now let's do a few retrievals...  Return player #1's high score.
lReturn = GetPrivateProfileInt("Player #1", "Score", -1, sFile)

Upon return:
    lReturn = 3500


Return player #2's high score.  Since there isn't a 2nd player, the default value will be returned.
lReturn = GetPrivateProfileInt("Player #2", "Score", -1, sFile)

Upon return:
    lReturn = -1


Fetch the skill level.
sString = String$(10, "*")
lSize = Len(sString)
lReturn = GetPrivateProfileString("Options", "Skill", "", sString, lSize, sFile)

Upon return:
    lReturn = 6
    sString = "Noviceø***"


Fetch all the Options entries.
sString = String$(30, "*")
lSize = Len(sString)
lReturn = GetPrivateProfileSection("Options", sString, lSize, sFile)

Upon return:
    lReturn = 24
    sString = "Sound=TrueøSkill=Noviceøø*****"


Fetch all the section names.
sString = String$(25, "*")
lSize = Len(sString)
lReturn = GetPrivateProfileString(vbNullString, "", "", sString, lSize, sFile)

Upon return:
    lReturn = 19
    sString = "OptionsøPlayer #1øø******"

Notice that lReturn contains the value 19 instead of the expected 18.  Normally, lReturn indexes the next-to-last character.  However, when retrieving the section names using GetPrivateProfileString or GetProfileString, lReturn indexes the last character.
Fetch all the keys in the Options section.
sString = String$(20, "*")
lSize = Len(sString)
lReturn = GetPrivateProfileString("Options", vbNullString, "", sString, lSize, sFile)

Upon return:
    lReturn = 12
    sString = "SoundøSkilløø*******"


Lastly, delete the sound option and all of player #1's information.
lReturn = WritePrivateProfileString("Options", "Sound", vbNullString, sFile)
lReturn = WritePrivateProfileString("Player #1", vbNullString, "", sFile)

Contents of MyINI.ini
[Options]
Skill=Novice

Case sensitivity

For the most part, section and key names are not case sensitive.  That is to say, if a section name is stored in the .INI file as "Options", you could read or write to that section by passing a section name of "OPTIONS" or "options" or even "OpTiOnS" - although I don't know why you'd want to :-)

Because of this case insensitivity, you usually can't have two sections or keys with the same name, such as "Options" and "OPTIONS".  For example, if you used WriteProfileString to create a key named "Skill", any attempt to add a second key named "SKILL" would only overwrite the value assigned to "Skill".  However, when using the WritePrivateProfileSection and WriteProfileSection APIs you could place duplicate key names into the list of entries, which would then get written to the specified section.  To retrieve the values for these duplicate keys, you'd have to use the GetPrivateProfileSection and GetProfileSection APIs.  For example:

sString = "Sound=True" & sNull _
        & "Sound=False" & sNull & sNull
lReturn = WritePrivateProfileSection("Options", sString, sFile)
results in:
[Options]
Sound=True
Sound=False
Adding duplicate section names is a little trickier.  Putting one or more trailing spaces at the end of a section or key name causes the APIs to distinguish between upper and lower case.  For example, consider the following API calls:
'No spaces after either "Options" or "Sound".
lReturn = WritePrivateProfileString("Options", "Sound", CStr(True), sFile)

'There's an extra space after "SOUND".
lReturn = WritePrivateProfileString("Options", "SOUND ", CStr(False), sFile)

'There's an extra space after "OPTIONS".
lReturn = WritePrivateProfileString("OPTIONS ", "Sound", CStr(True), sFile)

'There's an extra space after "OPTIONS" and "SOUND".
lReturn = WritePrivateProfileString("OPTIONS ", "SOUND ", CStr(False), sFile)

Executing these four API calls results in the .INI file containing the following:
[Options]
Sound=True
SOUND=False

[OPTIONS]
Sound=True
SOUND=False

In order to distinguish these section and key names when retrieving the values, you'll need to add the extra space to the section and key names when calling GetPrivateProfileString.

Even though the API calls exhibit unexpected behaviors, I don't suggest that these (or any other) undocumented "features" be utilized.  It would be a mistake to think that all future versions of the API will forever continue to allow such behavior.  I only mention these behaviors in case you experience some "weird" results when using these APIs - like I did when testing my code for this tutorial!

Accessing .INI files with "class"

One of the more unfortunate aspects of these API functions is that they use a special parameter value to yield special results.  An example of this is passing vbNullString to GetPrivateProfileString and GetProfileString in order to retrieve a list of section or key names.  Besides being a poor programming practice, the use of special parameter values can be confusing.

Another problem is the need to pre-size the string that will contain the return value.  It's difficult to know how long to make it.  There's really only one way to deal with this issue: keep increasing the length until the value fits.  To say the least, this situation is a nuisance.

A third problem is dealing with lists.  The API was written with languages like C++ in mind.  As a result, Visual Basic programmers have to deal with the special value of NULL in the return value.  If the API had been written for Visual Basic, lists would be returned as an array of strings, instead of a single NULL-delimited string.

To avoid these, and other, issues, I created two classes which encapsulate all the details of the API's idiosyncrasies.  The CPrivateINI class encapsulates the GetPrivateProfileInt, GetPrivateProfileSection, GetPrivateProfileString, WritePrivateProfileSection and WritePrivateProfileString API functions.  The CWinINI class encapsulates the GetProfileInt, GetProfileSection, GetProfileString, WriteProfileSection and WriteProfileString API functions.

You can download the classes and an accompanying test project here.  Accompanying the project is the file "Class Documentation.html".  This file, along with in-line documentation within the class modules, contains a description of the class modules' interfaces (public properties and methods).  You can view this documentation file online here.

The Test Project

There are ten forms in the test project.  The first is used to test CPrivateINI, the second (accessed through a menu option) is used to test CWinINI and the third gives a working example of what can happen when you change a key in the WIN.INI file.

When testing CPrivateINI, the first thing you must do is assign the name of a .INI file and click "Set INIFile":
If you do not do this, attempts to test other features will result in an error.

When browsing a .INI file, the first thing you may want to do is take a look at all the section names.  Simply click the "GetSections" buttons:

To view the keys of a particular section, enter the section's name and press either the "GetKeys" button:

To view all the entries of a particular section, enter the section's name and press either the "GetEntries" or "GetPairs" button:
I've chosen to show you the result of "GetEntries".  Selecting "GetPairs" shows the same data, only in a slightly different format.  Remember, both return the same data, only in a different way.

To view the value of a particular key, enter the section and key names then press either the "GetValue" or "GetLongValue" button:
I've chosen to press the "GetLongValue" button - only because the data is numeric.  In this case, pressing "GetValue" would have produced identical results.  However, if the value were not numeric, the results would probably be different.  Try it!  Enter a key name of "Name" and press the "GetLongValue" button.

To test the CWinINI class, we'll have to go to a different screen.  Select the menu option shown here:

As you can see, both forms look nearly identical.  In the following screen-shot, I've entered the section name "Desktop" and pressed the "GetEntries" button:

The only difference between the two is that the CWinINI test form does not have a place for you to enter the name of the .INI file - it's always WIN.INI.  Instead, there's a button labeled "Test Calculator Setting ..." - go ahead, press it:
Follow the instructions shown on the form to see how changing a setting in the WIN.INI file can alter the behavior of the Calculator program (calc.exe).

Conclusion

.INI files are very useful for saving user options and other data that your program wants to remember.  But accessing .INI files through the API can be difficult and confusing.  To help remove some of these hurdles, I created the CPrivateINI and CWinINI classes.  Add these files to your own projects or compile them into a .DLL - either way, I think you'll appreciate not having to deal directly with the API for .INI file access.

Brian P. Duckworth

<< Prev] [Main >>





Home | About | What's New | Source Code | FAQ | Tips & Tricks | Downloads | ToolBox | Tutorials | Game Programming | VB Award | Search | VB Forums | Feedback | VBNews | Copyright & Disclaimer | Advertise | Privacy Policy |

Quick searches: Site Search | Advanced Site Search 

Copyright 2002 by Exhedra Solutions, Inc.
By using this site you agree to its terms and conditions
VB Explorer and VBExplorer.com are trademarks of Exhedra Solutions, Inc.