Creating an INI File Editor

Now that you are familiar with the set of API functions that are used to access INI files, a sample program that better illustrates how the functions are used is in order. The program only uses the functions for accessing private INI files because it's not a good idea to casually fool around with the system INI file (WIN.INI).

The program that is created here will be built on later in this chapter in order to show how the API functions for accessing the System Registry are used. For now, however, we'll only concentrate on the INI functions.

To create the sample program, first build its user interface using Table 32.3, Table 32.4, and Figure 32.1 as guides. Table 32.3 lists the various controls that make up the interface, along with their properties. Table 32.4 lists the menu items that need to be added to the program's menu using VB's Menu Editor:

Table 32.3. The controls and their properties for the INI File/System Registry Editor program.

Control Type

Property

Value

Form Name frmINIEdit
  StartUpPosition 2 - Center Screen
Label Name lblSectionName
  Caption Section Name
  Height 255
  Left 240
  Top 120
  Width 1815
TextBox Name txtSectionName
  Enabled False
  Height 285
  Left 240
  Top 360
  Width 4575
Label Name lblKeyName
  Caption Key Name
  Height 255
  Left 240
  Top 720
  Width 1575
TextBox Name txtKeyName
  Enabled False
  Height 285
  Left 240
  Top 960
  Width 4575
Label Name lblValue
  Caption Value
  Height 255
  Left 240
  Top 2640
  Width 1575
TextBox Name txtValue
  Enabled False
  Height 285
  Left 240
  Top 1560
  Width 4575
CommandButton Name cmdRetrieve
  Caption Retrieve
  Enabled False
  Height 375
  Left 240
  Top 2040
  Width 1095
CommandButton Name cmdUpdate
  Caption Update
  Enabled False
  Height 375
  Left 1440
  Top 2040
  Width 1095
CommandButton Name cmdAdd
  Caption False
  Height 375
  Left 2640
  Top 2040
  Width 1095
CommandButton Name cmdDelete
  Caption False
  Height 375
  Left 3840
  Top 2040
  Width 1095
CommandButton Name cmdRefresh
  Caption False
  Height 375
  Left 5040
  Top 2040
  Width 1095
Label Name lblFileContents
  Caption INI File Contents
  Height 255
  Left 240
  Top 2640
  Width 1575
ListBox Name lstFileContents
  Height 2205
  Left 240
  Top 2880
  Width 5895
Label Name lblLastOpStatus
  Caption Last Operation Status
  Height 240
  Left 240
  Top 5280
  Width 1575
Label Name lblStatus
  Alignment 2 - Center
  BorderStyle 1 - Fixed Single
  Height 255
  Left 240
  Top 5520
  Width 5895
CommonDialog Name dlgGetFilename
  Left 5160
  Top 480
  FileName *.ini
  Filter *.ini
OptionButton Name optType
  Caption Registry
  Enabled False
  Height 255
  Index 1
  Left 5160
  Top 1440
  Value True
  Width 1215
OptionButton Name optType
  Caption INI File
  Enabled True
  Height 195
  Index 0
  Left 5160
  Top 1200
  Value False
  Width 1095

Table 32.4. The menu items for the sample program, to be added with VB's Menu Editor.

Menu Item Name

Caption

mnuFile File
mnuFileOpenINIFile Open INI File...
mnuExit Exit

Figure 32.1

The INI File/System Registry Editor program's user interface.

After the user interface has been designed, the program code can be added. But first, add a new module to the project so the API functions can be declared. Call the module INIEdit, then add the code in Listing 32.1.

Listing 32.1. - The code for the INIEdit module.

Public Declare Function GetPrivateProfileSection Lib "kernel32" _
    Alias "GetPrivateProfileSectionA" (ByVal lpAppName As String, _
    ByVal lpReturnedString As String, ByVal nSize As Long, _
    ByVal lpFileName As String) As Long
Public Declare Function GetPrivateProfileString Lib "kernel32" _
    Alias "GetPrivateProfileStringA" (ByVal lpApplicationName 
    As String,ByVal lpKeyName As Any, ByVal lpDefault As Strng, _
    ByVal lpReturnedString As String, ByVal nSize As Long, _
    ByVal lpFileName As String) As Long
Public Declare Function WritePrivateProfileSection Lib "kernel32" _
    Alias "WritePrivateProfileSectionA" (ByVal lpAppName As String, _
    ByVal lpString As String, ByVal lpFileName As String) As Long
Public Declare Function WritePrivateProfileString Lib "kernel32" _
    Alias "WritePrivateProfileStringA" (ByVal lpApplicationName 
    As String, ByVal lpKeyName As Any, ByVal lpString As Any, _
    ByVal lpFileName As String) As Long
Public gstrKeyValue As String * 256
Note that in the previous code, a global fixed-length string (gstrKeyValue) is also declared. This string will be used as the buffer for retrieving or setting key values using the API functions.

The next section of code to be added (Listing 32.2) is for the cmdAdd CommandButton control's Click event and INIAdd, the function that it calls to add a section or key to an INI file.

Listing 32.2. - The cmdAdd_Click event and the INIAdd function, which adds a section or key to the INI file.

Private Sub cmdAdd_Click()
' Make sure the user entered a section name.
If txtSectionName.Text = "" Then
    MsgBox "Please enter a section name first."
    Exit Sub
End IF
' For INI files, call the INIAdd function. For
' the Registry, call the RegAdd function.
If optType(0).Value = True Then
    lblStatus = INIAdd()
Else
    lblStatus = RegAdd()
End If
End Sub
Private Function INIAdd() As String
Dim lonStatus As Long
Dim strSectionName As String
' Remove brackets from section name, if necessary.
strSectionName = FixSectionName(txtSectionName.Text)
If txtKeyName.Text = "" Then<' No key name was specified, so add a new (empty) ' section with the WritePrivateProfileSection ' API function. lonStatus="WritePrivateProfileSection(strSectionName," _ "", dlgGetFilename.FileName) ' Determine the status of the operation. If lonStatus="0" Then INIAdd=""Error" Section could not be added" Else INIAdd=""Section" added succesfully" ReadFile End If Else ' Use the WritePrivateProfileString API function ' to add the key and, if necessary, the section. lonStatus="WritePrivateProfileString(strSectionName," _ txtKeyName.Text, txtValue.Text, dlgGetFilename.FileName) ' Determine the status of the operation. If lonStatus="0" Then INIAdd=""Error" Key could not be added" Else INIAdd=""Key" added successfully" ReadFile End If End If End Function 
The cmdAdd_Click event first ensures that a section name has been entered in the txtSectionName text box. It then calls one of two functions, INIAdd or RegAdd, based on the state of the INI File/Registry option buttons. Each function returns a string value that indicates the status of the operation. This status string is displayed in the lblStatus Label control. This format[md] checking the text boxes, calling separate routines for INI file or Registry operations, and then displaying the status[md]is used for all of the command buttons except the Refresh button, which is disabled when the Registry option button is selected.

The RegAdd function will be added later in this chapter when the Registry functions are discussed. No error can occur because the Registry option button is currently disabled.

The INIAdd function first ensures first calls the FixSectionName function, which removes brackets that enclose the section name. This function will be added later.

If no key name has been provided by the user in the txtKeyName text box, then it is assumed that a section is to be added. Because it has already been verified that a section name has been entered, the procedure can then use the WritePrivateProfileSection API function to create the new section. Note that the lpString argument for the API function is passed as an empty string (""), indicating that there is no list of keys to be updated.

If a key name has been provided, then the procedure uses the WritePrivateProfileString API function to add or update the key and its value. The key value is taken from the txtValue text box.

The name and path of the INI file, which is obtained using the CommonDialog control dlgGetFilename, is passed to the API functions as the lpFileName argument. It is the same way in the rest of the INI functions that compose the sample program.

After the WritePrivateProfileSection or WritePrivateProfileString functions are called, the status of the operation is returned by the INIAdd function. If the functions are successful, the contents of the INI file are re-displayed using the ReadFile procedure, which will also be added later.

The next section of program code (Listing 32.3) is for the Retrieve button. Again, two procedures are used: the cmdRetrieve_Click event and the INIRetrieve function.

Listing 32.3. - The cmdRetrieve_Click event and the INIRetrieve function, which retrieves a given key's value from the INI file.

 
Private Sub cmdRetrieve_Click()
Dim lonOpStatus As Long
' Make sure both a key name and a section name
' have been specified by the user.
If txtSectionName.Text = "" Then
    MsgBox "Please enter a section name first."
    Exit Sub
End If
If txtKeyName.Text = "" Then
    MsgBox "Please enter a key name first."
    Exit Sub
End If
' For INI files, call the INIRetrieve function. For
' the Registry, call the RegRetrieve function.
If optType(0).Value = True Then
    lblStatus = INIRetrieve()
Else
    lblStatus = RegRetrieve()
End If
End Sub
Private Function INIRetrieve() As String
Dim lonValueSize As Long
Dim strSectionName As String
' Remove brackets from section name, if necessary.
strSectionName = FixSectionName(txtSectionName.Text)
    
' Use the GetPrivateProfileString API function to
' retrieve the key's value from the file. A default
' value of "?!?" is used to see if the key has been
' retrieved successfully.
lonValueSize = GetPrivateProfileString(strSectionName, _
    txtKeyName.Text, "?!?", gstrKeyValue, 256, _
    dlgGetFilename.FileName)
    
' Determine the status of the operation. If the<' value of the key is the same as the default value ' ("?!?"), then the key was probably not found. If Left$(gstrKeyValue, 3)=""?!?"" Then INIRetrieve=""Error" Key could not be located" Else INIRetrieve=""Key" retrieved successfully" txtValue.Text="Left$(gstrKeyValue," lonValueSize) End If End Function 
You already know how the cmdRetrieve_Click event works, since it is very similar to the cmdAdd_Click event that was added earlier. Therefore, we'll press on to the discussion of the INIRetrieve function.

The first thing the INIRetrieve function does is strip the brackets from the section name using the FixSectionName function. This is pretty much standard in all of the program's INI functions.

The GetPrivateProfileString API function is used to retrieve the key's value. A default value of "?!?" is given so the success of the operation can be determined later. If the function places a value of "?!?" in the buffer (gstrKeyValue), it's a pretty safe bet that the key was not found.

The gstrKeyValue, which is used as the buffer in which the key value is to be placed by the function, was declared globally in the module that was added earlier. It is a fixed-length string of 256 characters, so a value of 256 is passed as the API function's nSize argument.

If the key is found (that is, a value of anything other than "?!?" is placed in gstrKeyValue), then the key value is displayed in the txtValue text box. Again, the status of the operation is returned by the function.

Next, add the code for the Update button (Listing 32.4). The cmdUpdate_Click event and the INIUpdate function are the procedures that are used.

Listing 32.4. - The cmdUpdate_Click event and the INIUpdate function, which updates a given key's value in the INI file.

Private Sub cmdUpdate_Click()
' Make sure both a key name and a section name
' have been specified by the user.
If txtSectionName.Text = "" Then
    MsgBox "Please enter a section name first."
    Exit Sub
End If
If txtKeyName.Text = "" Then
    MsgBox "Please enter a key name first."
    Exit Sub
End If
' For INI files, call the INIUpdate function. For
' the Registry, call the RegUpdate function.
If optType(0).Value = True Then
    lblStatus = INIUpdate
Else
    lblStatus = RegUpdate
End If
End Sub
Private Function INIUpdate() As String
Dim lonStatus As Long
Dim strSectionName As String
' Remove brackets from section name, if necessary
strSectionName = FixSectionName(txtSectionName.Text)
' Use the WritePrivateProfileString API function to
' update the key.
lonStatus = WritePrivateProfileString(strSectionName, _
    txtKeyName.Text, txtValue.Text, dlgGetFilename.FileName)
    
' Display the status of the operation and update
' the file contents list box if successful.
If lonStatus = 0 Then
    INIUpdate = "Error - Key could not be updated"
Else
    INIUpdate = "Key updated successfully"
    ReadFile
End If
End Function
If you've been following along, then the INIUpdate function will be pretty easy to figure out. First, the FixSectionName function strips the brackets from around the section name, if necessary. The WritePrivateProfileString function is then called and is passed the section name, key name, and value. The status of the operation is returned by the INIUpdate function and the contents of the INI file are re-read and displayed using the ReadFile function if the operation is successful.

The next command button to be coded is the Delete button. Add the code for the cmdDelete_Click event and the INIDelete function as shown in Listing 32.5:

Listing 32.5. - The cmdDelete_Click event and the INIDelete function, which deletes a key or section from the INI file.

Private Sub cmdDelete_Click()
' Make sure the user entered a section name.
If txtSectionName.Text = "" Then
    MsgBox "Please enter a section name first."
    Exit Sub
The cmdDelete_Click event is slightly different than the rest of the Click events so far. For INI files, it only checks to see if the section name has been entered. This is because it is possible to delete an entire section from the INI file, so a key name is not necessary. When dealing with the Registry, however, both the txtSectionName and txtKeyName text boxes need to be used.

The INIDelete event is perhaps a little more complicated than the other procedures, but it's still pretty straightforward. As always, the FixSectionName function is called. Then the txtKeyName text box is examined to see if a key name was entered. If not, it is assumed that the section is to be deleted. The user is prompted to ensure that this is really what was intended. If the user okays the section deletion, the strKeyName variable is set to vbNullString. Later, this variable will be passed to the WritePrivateProfileString API function. The vbNullString value tells the function that the entire section is to be deleted rather than an individual key.

If the txtKeyName text box does contain a key name, the name is assigned to the strKeyName variable. When that value is passed to the API function, the function knows that it is supposed to delete a single key.

After the strKeyName variable has been assigned, the WritePrivateProfileString API function is called. The status is returned by the INIDelete function. If the operation is successful, the txtKeyName and txtValue text boxes are cleared because the key no longer exists, and the contents of the INI file are re-displayed with the ReadFile function.

The last button event code to add is for the cmdRefresh_Click event (Listing 32.6):

Listing 32.6. - The cmdRefresh button's Click event, which simply uses the ReadFile function to re-display the INI file's contents.

Private Sub cmdRefresh_Click()
' Update the contents of the list box with the
' ReadFile procedure.
ReadFile
End Sub
The cmdRefresh_Click event simply calls the ReadFile procedure, which re-displays (refreshes) the contents of the INI file in the lstFileContents ListBox control.

Speaking of the ReadFile procedure, that is to be added next (Listing 32.7):

Listing 32.7. - The ReadFile procedure, used for reading the contents of the INI file and displaying them in a list box.

Private Sub ReadFile()
Dim intFileNum As Integer
Dim strLineIn As String
' Re-read the current INI file and display it
' in the list box lstFileContents.
lstFileContents.Clear
If Dir$(dlgGetFilename.FileName) <> "" Then
    intFileNum = FreeFile
    Open dlgGetFilename.FileName For Input As #intFileNum
    While Not EOF(1)
        Line Input #intFileNum, strLineIn
        lstFileContents.AddItem strLineIn
    Wend
    Close #intFileNum
End If
End Sub
This is another simple piece of code. The file name specified by the CommonDialog control's FileName property (dlgGetFilename.FileName) is read sequentially, with each line being added to the lstFileContents list box.

Another procedure that has been used often in the functions added earlier is the FixSectionName procedure (Listing 32.8):

Listing 32.8. - THe FixSectionName function, used to remove the brackets from around a section name.

Private Function FixSectionName(sOldName As String) As String
Dim strNewName As String
' Remove the [brackets] around the section name
' field. The Windows API functions add the brackets
' for you.
If Left$(sOldName, 1) = "[" Then
    strNewName = Mid$(sOldName, 2)
Else
    strNewName = sOldName
End If
If Right$(sOldName, 1) = "]" Then
    strNewName = Left$(strNewName, Len(strNewName) - 1)
End If
FixSectionName = strNewName
End Function
Again, this procedure is pretty simple. The left and right brackets are removed from the section name that is passed to the function, and the new section name is returned by the function.

This function is necessary because the API functions that write sections add the brackets automatically. If you already have the brackets around the section name, the result will be a section name with double brackets added to the INI file.

Because the contents of the INI file are displayed in a ListBox control, it would be nice to make use of the situation and have a key or section name appear in the program's text box fields when it is clicked on. In order to facilitate such a feature, code needs to be added to the lstFileContents_Click event (Listing 32.9):

Listing 32.9. - The lstFileContents_Click event, which transfers a key or section name to the program's text boxes when it is clicked on in the list box.

Private Sub lstFileContents_Click()
Dim intEqualPos As Integer
Dim lonListPtr As Long
Dim strListItem As String
txtKeyName.Text = ""
txtValue.Text = ""
' Get the item that was chosen from the list box.
strListItem = lstFileContents.List(lstFileContents.ListIndex)
' Did the user click on a section name (enclosed in
' brackets) or a key name?
If Left$(LTrim$(strListItem), 1) = "[" Then
    txtSectionName.Text = strListItem
Else
    txtSectionName.Text = ""
    ' Separate the key info into the key name and
    ' its value.
    intEqualPos = InStr(strListItem, "=")
    If intEqualPos > 1 Then
        txtKeyName.Text = Left$(strListItem, intEqualPos - 1)
        txtValue.Text = Mid$(strListItem, intEqualPos + 1)
    End If
    ' Loop backwards through the list and try to<' find the section name to which the key belongs. For lonListPtr="lstFileContents.ListIndex" To 0 Step 1 strListItem="LTrim$(lstFileContents.List(lonListPtr))" If Left$(strListItem, 1)=""["" Then txtSectionName.Text="strListItem" Exit For End If Next lonListPtr End If End Sub 
The first thing this procedure needs to determine is if the user clicked on a section name or a key name. This is easy to figure out because section names are always enclosed in brackets.

If a section name is clicked on, the name of the section is placed in the txtSectionName control and the procedure ends. However, if a key name is clicked on, things are a little more complicated. The key name and it's value have to be separated, because they are listed in the INI file using the format key=value. Also, it is a good idea to determine the section in which the key is contained. This requires looping back through the INI file contents to find the first section name, which is placed in txtSectionName.

The next procedures that need to be added to the program are for the two menu items (Listing 32.10):

Listing 32.10. - The code for the program's menu items.

Private Sub mnuExit_Click()
' Exit the program.
Unload Me
End
End Sub
Private Sub mnuFileOpenINIFile_Click()
Dim intFileNum As Integer
Dim strFileName As String
' Show the Open File dialog box
dlgGetFilename.FileName = "*.ini"
dlgGetFilename.Filter = "*.ini"
dlgGetFilename.ShowOpen
' If a file was selected, then enable some of the
' components of the user interface. Then read in
' and display the contents of the file.
strFileName = dlgGetFilename.FileName
If strFileName <> "" And strFileName <> "*.ini" Then
    txtSectionName.Enabled = True
    txtKeyName.Enabled = True
    txtValue.Enabled = True
    cmdRefresh.Enabled = True
    ' If no file for the selected name exists, 
    ' make one.
    If Dir$(strFileName) = "" Then
        intFileNum = FreeFile
        Open strFileName For Output As #intFileNum
        Close #1
    End If
    ' Show the file name in the status area.
    lblStatus = "File " & strFileName & " opened."
    ReadFile
    
End If
End Sub
The mnuExit_Click routine simply ends the program. The mnuFileOpenINIFile_Click routine uses the dlgGetFilename CommonDialog control to determine the name and path of the INI file that is to be edited. If the file is found, its contents are displayed using the ReadFile procedure added earlier.

Because none of the API functions are valid until at least the name of a section has been entered, code needs to be added to keep the user from clicking on any of the command buttons until they have entered a section or key name. The last three procedures for the sample program (Listing 32.11) implement this safety measure:

Listing 32.11. - Procedures needed to ensure that no command buttons are used until an INI file has been selected.

Private Sub txtKeyName_Change()
' Check the status of the txtKeyName and
' txtSectionName fields to see if some command
' buttons can be enabled.
ToggleCmdButtons
End Sub
Private Sub txtSectionName_Change()
' Check the status of the txtKeyName and
' txtSectionName fields to see if some command
' buttons can be enabled.
ToggleCmdButtons
End Sub
Private Sub ToggleCmdButtons()
' Disable/enable certain command buttons based on
' whether or not the txtSectionName and txtKeyName
' fields are blank.
If txtSectionName.Text = "" And txtKeyName.Text = "" Then

That's all there is to the sample program. When you try it out, it’s probably a good idea to create a new INI file just for fooling around with. Select the Open INI File option from the File menu and type in the name of an INI file that doesn't already exist, such as VBU-CH32.INI.

Try experimenting with the Add, Update, Retrieve, and Delete buttons. Just remember that you have to enter at least a section name before choosing any of the options.

If you haven't already, make sure you save the program because it will be used in the next section for performing System Registry editing functions.

Top Home