I know this subject has been beaten to death and beyond, but I have not seen a decent solution to this problem. Sure one could write a file parser to cash the file into memory using collections or what not. One could also easily write a module to re-represent the API calls needed to work with these files. There is a better way.
I created a solution for this task years ago, one that is slightly better than any I've seen even though it lacks their thuroughness. It uses a class named cIniFile to represent INI files as objects. My needs never went beyond getting numbers and strings so the class is very small, one property and four methods are all I ever needed. The filename property provides the object with its link to the physical file. GetString and PutString provide the string acess. Finally GetNumber and PutNumber provide access to INI file number values. I thought of adding StringVal and NumberVal properties, but felt it overkill and better left to customized classes to be built later.
Using a class allows me to create other objects with the same interface to store the values in a database or even the registry without needing extensive changes in the main line code.
The basic low level design of this class make it ideal for developing classes using it to create endless possibilities. Default filenames could be assigned in the class_initialize method. Properties can be used to represent INI-file sections, the gets read from the file, the lets write. The embedded cIniFile object can be exposed, made public, assuring that all necessary access remains available...
I've put the class code last to make it easier to get. The sample program uses the same ini file and keys so watching the changes requires a debugger and single stepping.
Sample 1 Basic use of cIniFile: ----- Sub main() Dim oIni As cINIFile Dim oCustIni as New cCustINI Set oIni = New cINIFile
Debug.Print .GetNumber("System", "FirstNum", -1) Debug.Print .GetString("System", "Type") End With With oCustIni .mSystem("Type") = "Customized Sample" .mSystem("FirstNum") = 2 Debug.Print .mSystem("FirstNum") Debug.Print .mSystem("Type") End With With oCustIni.oIni .PutString "System", "Type", "Customized Sample File2" .PutNumber "System", "FirstNum", 5
Debug.Print .GetNumber("System", "FirstNum", -1) Debug.Print .GetString("System", "Type") End With
Set oIni = Nothing End Sub -----
Sample 2 -- Customized Class cCustIni ----- Option Explicit Public oINI as cIniFile
Private Sub Class_Initialize oINI = New cIniFile oINI.Filename = App.Path & "\Config.ini" End Sub
Private Sub Class_Terminate oINI = Nothing End Sub
Public Property Get mSystem(sKey as String) Dim ls0 as String With oINI If LCase$(Mid$(sKey, 2, 1)) = "i" then mSystem = .GetNumber("System", sKey) ElseIf LCase$(Mid$(sKey, 2, 1)) = "b" then ls0 = .GetString("System", sKey) mSystem = lcase(ls0) = "true" Else mSystem = .GetString("System", sKey) End If End With End Property
Public Property Let mSystem(sKey as String, v) Dim ls0 as String If "String" <> TypeName(v) then ls0 = cStr(v) Else ls0 = v End If
oINI.PutString "System", sKey, ls0 End Property -----
cIniFile CLASS CODE Copy and past into a class module. For the example above to work, make sure the class is named cIniFile.
Option Explicit Private Declare Function GetPrivateProfileInt Lib "kernel32" Alias "GetPrivateProfileIntA" (ByVal lpApplicationName As String, ByVal lpKeyName As String, ByVal nDefault As Long, ByVal lpFileName As String) As Long Private Declare Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Long, ByVal lpFileName As String) As Long Private 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
Private sFile As String
Public Property Let Filename(ByVal vNewValue As Variant) sFile = vNewValue End Property
Public Function GetString(sSection As String, sKey As String, Optional sDefault As String = "None") As String Dim ls0 As String ls0 = Space(1024)
GetPrivateProfileString sSection, sKey, sDefault, ls0, 1024, sFile If InStr(ls0, Chr(0)) Then ls0 = Left$(ls0, InStr(ls0, Chr(0)) - 1) End If GetString = ls0 End Function
Public Function GetNumber(sSection As String, sKey As String, Optional lDefault As Long = 0) As Long Dim ln0 As Long ln0 = GetPrivateProfileInt(sSection, sKey, lDefault, sFile) GetNumber = ln0 End Function
Public Function PutString(sSection As String, sKey As String, sValue As String) WritePrivateProfileString sSection, sKey, sValue, sFile End Function
Public Function PutNumber(sSection As String, sKey As String, lValue As Long) Dim ls0 As String ls0 = Str(lValue) ls0 = Trim$(ls0) WritePrivateProfileString sSection, sKey, ls0, sFile End Function