Working with Files

31/07/2009 14:59

Working with INI FILES

ReadINI

VBScript Code:
Function ReadIni( myFilePath, mySection, myKey )
    ' This function returns a value read from an INI file
    '
    ' Arguments:
    ' myFilePath  [string]  the (path and) file name of the INI file
    ' mySection   [string]  the section in the INI file to be searched
    ' myKey       [string]  the key whose value is to be returned
    '
    ' Returns:
    ' the [string] value for the specified key in the specified section
    '
    ' CAVEAT:     Will return a space if key exists but value is blank
    '
    ' Written by Keith Lacelle
    ' Modified by Denis St-Pierre and Rob van der Woude

    Const ForReading   = 1
    Const ForWriting   = 2
    Const ForAppending = 8

    Dim intEqualPos
    Dim objFSO, objIniFile
    Dim strFilePath, strKey, strLeftString, strLine, strSection

    Set objFSO = CreateObject( "Scripting.FileSystemObject" )

    ReadIni     = ""
    strFilePath = Trim( myFilePath )
    strSection  = Trim( mySection )
    strKey      = Trim( myKey )

    If objFSO.FileExists( strFilePath ) Then
        Set objIniFile = objFSO.OpenTextFile( strFilePath, ForReading, False )
        Do While objIniFile.AtEndOfStream = False
            strLine = Trim( objIniFile.ReadLine )

            ' Check if section is found in the current line
            If LCase( strLine ) = "[" & LCase( strSection ) & "]" Then
                strLine = Trim( objIniFile.ReadLine )

                ' Parse lines until the next section is reached
                Do While Left( strLine, 1 ) <> "["
                    ' Find position of equal sign in the line
                    intEqualPos = InStr( 1, strLine, "=", 1 )
                    If intEqualPos > 0 Then
                        strLeftString = Trim( Left( strLine, intEqualPos - 1 ) )
                        ' Check if item is found in the current line
                        If LCase( strLeftString ) = LCase( strKey ) Then
                            ReadIni = Trim( Mid( strLine, intEqualPos + 1 ) )
                            ' In case the item exists but value is blank
                            If ReadIni = "" Then
                                ReadIni = " "
                            End If
                            ' Abort loop when item is found
                            Exit Do
                        End If
                    End If

                    ' Abort if the end of the INI file is reached
                    If objIniFile.AtEndOfStream Then Exit Do

                    ' Continue with next line
                    strLine = Trim( objIniFile.ReadLine )
                Loop
            Exit Do
            End If
        Loop
        objIniFile.Close
    Else
        WScript.Echo strFilePath & " doesn't exists. Exiting..."
        Wscript.Quit 1
    End If
End Function
 
Requirements:
Windows version: any
Network: any
Client software: N/A
Script Engine: any
Summarized: Works in any Windows version, with any engine.
 
[Back to the top of this page]
 
WriteINI
VBScript Code:
Sub WriteIni( myFilePath, mySection, myKey, myValue )
    ' This subroutine writes a value to an INI file
    '
    ' Arguments:
    ' myFilePath  [string]  the (path and) file name of the INI file
    ' mySection   [string]  the section in the INI file to be searched
    ' myKey       [string]  the key whose value is to be written
    ' myValue     [string]  the value to be written (myKey will be
    '                       deleted if myValue is <DELETE_THIS_VALUE>)
    '
    ' Returns:
    ' N/A
    '
    ' CAVEAT:     WriteIni function needs ReadIni function to run
    '
    ' Written by Keith Lacelle
    ' Modified by Denis St-Pierre, Johan Pol and Rob van der Woude

    Const ForReading   = 1
    Const ForWriting   = 2
    Const ForAppending = 8

    Dim blnInSection, blnKeyExists, blnSectionExists, blnWritten
    Dim intEqualPos
    Dim objFSO, objNewIni, objOrgIni, wshShell
    Dim strFilePath, strFolderPath, strKey, strLeftString
    Dim strLine, strSection, strTempDir, strTempFile, strValue

    strFilePath = Trim( myFilePath )
    strSection  = Trim( mySection )
    strKey      = Trim( myKey )
    strValue    = Trim( myValue )

    Set objFSO   = CreateObject( "Scripting.FileSystemObject" )
    Set wshShell = CreateObject( "WScript.Shell" )

    strTempDir  = wshShell.ExpandEnvironmentStrings( "%TEMP%" )
    strTempFile = objFSO.BuildPath( strTempDir, objFSO.GetTempName )

    Set objOrgIni = objFSO.OpenTextFile( strFilePath, ForReading, True )
    Set objNewIni = objFSO.CreateTextFile( strTempFile, False, False )

    blnInSection     = False
    blnSectionExists = False
    ' Check if the specified key already exists
    blnKeyExists     = ( ReadIni( strFilePath, strSection, strKey ) <> "" )
    blnWritten       = False

    ' Check if path to INI file exists, quit if not
    strFolderPath = Mid( strFilePath, 1, InStrRev( strFilePath, "\" ) )
    If Not objFSO.FolderExists ( strFolderPath ) Then
        WScript.Echo "Error: WriteIni failed, folder path (" _
                   & strFolderPath & ") to ini file " _
                   & strFilePath & " not found!"
        Set objOrgIni = Nothing
        Set objNewIni = Nothing
        Set objFSO    = Nothing
        WScript.Quit 1
    End If

    While objOrgIni.AtEndOfStream = False
        strLine = Trim( objOrgIni.ReadLine )
        If blnWritten = False Then
            If LCase( strLine ) = "[" & LCase( strSection ) & "]" Then
                blnSectionExists = True
                blnInSection = True
            ElseIf InStr( strLine, "[" ) = 1 Then
                blnInSection = False
            End If
        End If

        If blnInSection Then
            If blnKeyExists Then
                intEqualPos = InStr( 1, strLine, "=", vbTextCompare )
                If intEqualPos > 0 Then
                    strLeftString = Trim( Left( strLine, intEqualPos - 1 ) )
                    If LCase( strLeftString ) = LCase( strKey ) Then
                        ' Only write the key if the value isn't empty
                        ' Modification by Johan Pol
                        If strValue <> "<DELETE_THIS_VALUE>" Then
                            objNewIni.WriteLine strKey & "=" & strValue
                        End If
                        blnWritten   = True
                        blnInSection = False
                    End If
                End If
                If Not blnWritten Then
                    objNewIni.WriteLine strLine
                End If
            Else
                objNewIni.WriteLine strLine
                    ' Only write the key if the value isn't empty
                    ' Modification by Johan Pol
                    If strValue <> "<DELETE_THIS_VALUE>" Then
                        objNewIni.WriteLine strKey & "=" & strValue
                    End If
                blnWritten   = True
                blnInSection = False
            End If
        Else
            objNewIni.WriteLine strLine
        End If
    Wend

    If blnSectionExists = False Then ' section doesn't exist
        objNewIni.WriteLine
        objNewIni.WriteLine "[" & strSection & "]"
            ' Only write the key if the value isn't empty
            ' Modification by Johan Pol
            If strValue <> "<DELETE_THIS_VALUE>" Then
                objNewIni.WriteLine strKey & "=" & strValue
            End If
    End If

    objOrgIni.Close
    objNewIni.Close

    ' Delete old INI file
    objFSO.DeleteFile strFilePath, True
    ' Rename new INI file
    objFSO.MoveFile strTempFile, strFilePath

    Set objOrgIni = Nothing
    Set objNewIni = Nothing
    Set objFSO    = Nothing
    Set wshShell  = Nothing
End Sub
 
Requirements:
Windows version: any
Network: any
Client software: Requires the ReadINI function
Script Engine: any
Summarized: Works in any Windows version, with any engine; requires the ReadINI function.
 
[Back to the top of this page]
 
DeleteINI
To delete a key in an INI file, use WriteINI with a value "<DELETE_THIS_VALUE>".
 
[Back to the top of this page]
 
Sample Script
VBScript Code:
WriteIni "C:\test.ini", "TEST1", "My1stKey", "My1stValue"
WriteIni "C:\test.ini", "TEST2", "My1stKey", "My1stValue"
WScript.Echo ReadIni( "C:\test.ini", "TEST1", "My1stKey" )
WriteIni "C:\test.ini", "TEST1", "My1stKey", "My2ndValue"
WScript.Echo ReadIni( "C:\test.ini", "TEST1", "My1stKey" )
 
Sample Output:
My1stValue
My2ndValue
 

 

 

 

XML

To test the code on this page yourself will require some preparation.

I will be using an XML file created by exporting data from DVD Profiler. So you may want to download the free version first, and start creating your own database.
As an alternative, you can copy and paste this single record from my own DVD database, but it doesn't demonstrate the queries quite as good as a "real" populated database does.

Next you may want to download and install Microsoft's free XML Notepad 2007.
It can be used to display the XML file in tree view and XSL view, which makes it easier to see and understand the queries we are going to build.
Or as alternatives, download XMLFox, or use your browser, or Notepad, or any other text or programmer's editor (or for the diehards: TYPE and MORE).

 

Checklist:
 

 

Let's get started.
In your editor, IDE or whatever, type the following code:

Set xmlDoc = CreateObject( "Microsoft.XMLDOM" )
xmlDoc.Async = "False"
xmlDoc.Load( "dvdcoll.xml" )

If you have an integrated object browser, you'll probably see a reference to MSXML being inserted.

We'll start with simple query, display each DVD's title:

strQuery = "/Collection/DVD/Title"
Set colNodes = xmlDoc.selectNodes( strQuery )
For Each objNode in colNodes
WScript.Echo objNode.nodeName & ": " & objNode.text
Next

The result will look like this:

Title: Wild Down Under
Title: Pride
Title: Gandhi
Title: The Abyss
Title: Shouf Shouf Habibi

So, why was the query built like this?

DVD Profiler screenshot

Take a look at the image on the right, a fragment of a screenshot of XML Notepad's "XSL Output" window with our XML file loaded (or open the XML file in a plain text editor).

What we see is an opening tag <Collection> as the first tag. This translates to /Collection in the query.
Then, between the opening <Collection> and closing </Collection> tags ("inside" the Collection), note the "recordsets" of <DVD> and </DVD> tags. These translate to the DVD part of the query: in the root we find Collections, and inside (or under) Collections we find DVD -- just like a directory structure on a disk.
Inside (or under) DVD there are several tags, Title being one of them. So now we have /Collection/DVD/Title.

Note: Queries are case sensitive! Capitalization must match the XML tags exactly.

Now let's move to the next level of complexity for our queries, display multiple properties:

strQuery = "/Collection/DVD/ ( Title | Genres )"
Note: This is the query definition only. Replace the previous query definition by this new one. The rest of the script remains unaltered.

The part of the query between parentheses is a list of choices, separated by "pipe" characters (|).
Tags will be listed if they match any of these choices, so Title as well as Genres will be shown for each DVD in the collection.

The result will look like this:

Title: Wild Down Under
Genres: Documentary Special Interest
Title: Pride
Genres: Documentary
Title: Gandhi
Genres: Drama Classic
Title: The Abyss
Genres: Science-Fiction Suspense/Thriller
Title: Shouf Shouf Habibi
Genres: Comedy

The genres are listed as single lines, where each line can contain one (or zero) or more genres.

If you look further down in the XML structure, you'll notice that some keys or tags have subkeys: Genres for example uses Genre subkeys.
Subkeys are used when multiple values are possible. They can be viewed best in XML Notepad's treeview.
To list each individual genre we must use /Collection/DVD/Genres/Genre.

So next, let's display the Title and each individual Genre:

strQuery = "/Collection/DVD/ ( Title | Genres/Genre )"

Roughly translated: display anything that matches "/Collection/DVD/Title" or "/Collection/DVD/Genres/Genre"

The result will look like this:

Title: Wild Down Under
Genre: Documentary
Genre: Special Interest
Title: Pride
Genre: Documentary
Title: Gandhi
Genre: Drama
Genre: Classic
Title: The Abyss
Genre: Science-Fiction
Genre: Suspense/Thriller
Title: Shouf Shouf Habibi
Genre: Comedy

Notice how some DVDs have multiple Genres.

OK, listing properties for each item in a collection no longer has any secrets for you.
Let's select specific items (DVDs) from the collection.
List the titles and genres of all documentaries:

strQuery = "/Collection/DVD [ Genres/Genre = 'Documentary' ] / ( Title | Genres/Genre )"

The result will look like this:

Title: Wild Down Under
Genre: Documentary
Genre: Special Interest
Title: Pride
Genre: Documentary
Title: March of the Penguins
Genre: Documentary
Title: Alaska: Spirit of the Wild
Genre: Documentary
Genre: Special Interest
Title: Wilderness Journey - Canyon Suites
Genre: Documentary
Genre: Special Interest
Genre: Music

It is possible to select based on genre without displaying it:

strQuery = "/Collection/DVD [ Genres/Genre = 'Documentary' ] /Title"

would only display the titles of the documentaries.

Finally, the selection can be narrowed down by using and and or. Note that these are case sensitive, so And will not work.

List titles and all formats for each documentary that supports 16x9 format:

strQuery = "/Collection/DVD " _
& "[ Genres/Genre = 'Documentary' and Format/Format16X9 = 'True' ] " _
& "/ ( Title | Format/* )"

The result will look like this:

Title: Wild Down Under
FormatAspectRatio: 1.78
FormatVideoStandard: PAL
FormatLetterBox: True
FormatPanAndScan: False
FormatFullFrame: False
Format16X9: True
FormatDualSided: False
FormatDualLayered: False
FormatFlipper: False
Title: Pride
FormatVideoStandard: PAL
FormatLetterBox: True
FormatPanAndScan: False
FormatFullFrame: True
Format16X9: True
FormatDualSided: False
FormatDualLayered: True
FormatFlipper: False

 

Self-Destruct

 

WSH (WScript.ScriptFullName)
VBScript Code:
' Author:   Denis St-Pierre
' Function: Make a script delete itself
Set objFSO = CreateObject( "Scripting.FileSystemObject" )
objFSO.DeleteFile WScript.ScriptFullName
WScript.Quit
Requirements:
Windows version: any
Network: any
Client software: N/A
Script Engine: WSH
Summarized: Works in any Windows version, with WSH script engine (WSCRIPT.EXE and CSCRIPT.EXE).
 
Back