Visual Basic bietet Ihnen die Möglichkeit an, Konfigurationsdaten mittels der Anweisungen bzw. Funktionen SaveSetting, GetSetting, GetAllSettings und DeleteSetting in der Windows-Registrierung für den aktuell aktiven Anwender unter einfach handzuhabenden Schlüsselnamen abzulegen und zu verwalten. Eine Alternative dazu ist die Ablage von Konfigurationsdaten in so genannten INI-Dateien. Diese sind unter den frühen Windows-Versionen 3.x üblich gewesen und erlauben ebenfalls eine Ablage von Daten unter Schlüsselnamen ohne komplizierte Dateioperationen.
Im Gegensatz zur Windows-Registrierung (auch bei den oben genannten VB-Funktionen, siehe "Verzweigte Settings") lassen sich Inhalte in INI-Dateien nur einstufig hierarchisch gliedern. Sie sind nach einem einfacheren Schema aufgebaut und können in jedem Text-Editor (Notepad usw.) betrachtet und modifiziert werden. Auf einen in einer Zeile eckige Klammern gesetzten beliebigen Abschnittsnamen ("Section") folgen zeilenweise beliebig viele Paare aus einem Schlüsselnamen ("Key") und den dazu abzulegenden Daten ("Setting") im String-Format.
[Section1]
Key1=Setting1
Key2=Setting2
...
KeyX=SettingX
[Section2]
Key1=Setting1
Key2=Setting2
...
KeyX=SettingX
[...]
...
[SectionX]
...
Ähnlich den VB-Funktionen zur Ablage in der Registrierung dienen Windows-API-Funktion zum Schreiben in und Lesen aus solcherart aufgebauten INI-Dateien. Abgesehen davon, dass der Umgang mit diesen API-Funktionen in VB nicht ganz einfach ist, folgen sie dennoch dem gleichen Prinzip: Abschnitte mit Paaren aus Schlüsseln und Werten. Da die hierarchische Tiefe fehlt, entfällt die Gliederungsebene "AppName", die sich durch Speicherung in für jede Anwendung eigenen Dateien ersetzen lässt.
Aufgrund dieser Verwandtschaft bietet es sich an, die vorhandenen VB-Anweisungen und Funktionen ein klein wenig so zu modifizieren, dass Sie sie sowohl für die Ablage in der Registrierung als auch in INI-Dateien verwenden können. Natürlich können Sie dazu nicht in VB-Interna eingreifen. Jedoch haben Sie die Möglichkeit, mit in Standard-Modulen angelegten Prozeduren und Funktionen originale VB-Anweisungen und Funktionen zu ersetzen (zu "überschreiben"). Wenn Sie dabei die Struktur der originalen Parameter (so denn welche vorhanden sind) beibehalten, und allenfalls um weitere, meistens optional anzulegende Parameter erweitern, gibt es eigentlich keine Probleme.
Schauen wir uns als erstes einmal die Modifikation der Anweisung SaveSetting an.
Private Declare Function WritePrivateProfileString Lib "kernel32" _
Alias "WritePrivateProfileStringA" (ByVal Section As String, _
ByVal Key As String, ByVal Setting As String, _
ByVal FileName As String) As Long
Public Sub SaveSetting(AppName As String, Section As String, _
Key As String, Setting As String, _
Optional ByVal ToIniFile As Boolean)
Dim nRet As Long
If ToIniFile Then
nRet = WritePrivateProfileString(Section, Key, Setting, AppName)
If nRet = 0 Then
Err.Raise Err.LastDllError, "SaveSetting [INI-File]", _
"System Error Code"
End If
Else
VBA.SaveSetting AppName, Section, Key, Setting
End If
End Sub
Sie sehen die gleichen Parameter in der originalen Abfolge und mit den originalen Datentypen. Hinzugekommen ist der optionale Parameter ToIniFile. Sein Vorgabewert ist False. Wird der Parameter wie beim gewohnten Aufruf von SaveSetting weggelassen, wird in der Prozedur so verzweigt, dass die originale SaveSetting-Anweisung (aus der VBA-Bibliothek) aufgerufen wird. Wird jedoch der Wert True übergeben, wird mittels der API-Funktion WritePrivateProfileString in eine INI-Datei geschrieben. Deren Pfad wird hier im ersten Parameter AppName übergeben.
Gibt die API-Funktion den Wert 0 zurück, konnte nicht in die angegebene INI-Datei geschrieben werden. Eine mögliche Ursache wäre, dass das im Pfad angegebene Verzeichnis nicht existiert. In jedem Fall wird ein das Problem näher aufschlüsselnder Fehler-Code in der Eigenschaft LastDllError des Err-Objekts abgelegt, der als Laufzeitfehler zum übergeordneten Aufruf weitergeleitet wird. Um das Vorhandensein der betreffenden INI-Datei selbst brauchen Sie sich nicht zu kümmern. Sie wird gegebenenfalls unter dem angegebenen Namen und Pfad automatisch angelegt. Fehlt die Pfadangabe völlig, und Sie übergeben nur einen reinen Dateinamen, wird die INI-Datei automatisch im Windows-Verzeichnis angelegt. Üblich ist die Dateierweiterung ".ini" - bindend und verpflichtend ist diese Erweiterung jedoch nicht. Allenfalls erleichtert die konventionelle Erweiterung das einfachere Öffnen in einem Editor.
Als Gegenstück zum Auslesen der abgelegten Daten behalten wir die Funktion GetSetting bei. Auch ihren Parameter lassen wir unangetastet und ergänzen wieder nur einen optionalen Parameter: FromIniFile.
Private Declare Function GetPrivateProfileString Lib "kernel32" _
Alias "GetPrivateProfileStringA" (ByVal Section As String, _
ByVal Key As String, ByVal Default As String, ByVal Buffer As String, _
ByVal Size As Long, ByVal FileName As String) As Long
Public Function GetSetting(AppName As String, Section As String, _
Key As String, Optional Default As String, _
Optional ByVal FromIniFile As Boolean) As String
Dim nBuffer As String
Dim nRet As Long
Const kBufferSize = 1024
If FromIniFile Then
If Len(Trim$(AppName)) = 0 Then
GetSetting = Default
Exit Function
ElseIf Len(Trim$(Section)) = 0 Then
GetSetting = Default
Exit Function
ElseIf Len(Trim$(Key)) = 0 Then
GetSetting = Default
Exit Function
End If
nBuffer = Space$(kBufferSize)
nRet = GetPrivateProfileString(Section, Key, Default, nBuffer, _
kBufferSize, AppName)
GetSetting = Left$(nBuffer, nRet)
Else
GetSetting = VBA.GetSetting(AppName, Section, Key, Default)
End If
End Function
Und auch hier wird beim Fehlen des Parameters FromIniFile bzw. bei Übergabe des Wertes False das VB-Original aufgerufen. Der Aufruf der entsprechenden API-Funktion GetPrivateProfileString ist dagegen ein wenig komplizierter. Sie gibt nämlich nicht einfach einen String als zurück, wie Sie es wohl von einer VB-Funktion her gewohnt sind. Vielmehr erwartet sie in einem eigens dafür vorgesehenen Parameter (Puffer, "Buffer") einen leeren String, in dem nach dem Aufruf der gesuchte Wert vorzufinden ist. Lediglich die Länge des dort hinein geschriebenen Strings ist dem Rückgabewert der Funktion zu entnehmen. Diese Länge brauchen wir allerdings auch, um den ursprünglich in ausreichender Überlänge (den Konstantwert 1024 hierfür können Sie natürlich Ihren Bedürfnissen anpassen) übergebenen Puffer-String auf die Länge des tatsächlichen Strings zu kürzen.
Wie beim VB-Original können Sie auch hier einen Vorgabewert ("Default") festlegen, der immer dann zurückgegeben wird, wenn eine der Angaben in den übrigen Parametern nicht zu einem Wert in der angegebenen INI-Datei führen oder die Datei selbst noch gar nicht existieren sollte.
Auch bei INI-Dateien gibt es die Möglichkeit, einen ganzen Abschnitt auf einen Schlag auszulesen und wie bei der VB-Funktion GetAllSettings als zweidimensionales Array zurückgeben zu lassen.
Private Declare Function GetPrivateProfileSection Lib "kernel32" _
Alias "GetPrivateProfileSectionA" (ByVal Section As String, _
ByVal Buffer As String, ByVal Size As Long, _
ByVal FileName As String) As Long
Public Function GetAllSettings(AppName As String, Section As String, _
Optional ByVal FromIniFile As Boolean) As Variant
Dim nBuffer As String
Dim nRet As Long
Dim nItems() As String
Dim nItem() As String
Dim l As Long
Dim nSettings As Variant
Const kBufferSize = 32767
If FromIniFile Then
nBuffer = Space$(kBufferSize)
nRet = _
GetPrivateProfileSection(Section, nBuffer, kBufferSize, AppName)
nItems = Split(Left$(nBuffer, nRet), Chr$(0))
If Len(nItems(UBound(nItems))) Then
ReDim nSettings(0 To UBound(nItems), 0 To 1)
Else
ReDim nSettings(0 To UBound(nItems) - 1, 0 To 1)
End If
For l = 0 To UBound(nSettings)
nItem = Split(nItems(l), "=")
nSettings(l, 0) = nItem(0)
nSettings(l, 1) = nItem(1)
Next 'l
GetAllSettings = nSettings
Else
GetAllSettings = VBA.GetAllSettings(AppName, Section)
End If
End Function
Den Aufruf reichen wir wieder an das VB-Original weiter, wenn die Angabe FromIniFile fehlt. Ansonsten rufen wir die API-Funktion GetPrivateProfileSection auf, die im Prinzip wie zuvor die Funktion GetPrivatePoifileString funktioniert. Der Unterschied ist, dass zum einen kein einzelner Schlüssel und kein Vorgabewert angegeben wird, und dass im Puffer die einzelnen Paare aus Schlüssel und zugehörigem Wert des angegebenen Abschnitts hintereinander durch Null-Zeichen (Ascii-Wert 0) von einander getrennt abgelegt sind. Die Aufteilung in einzelne Paar-Strings erledigen wir mit der VB-Funktion Split (ab VB 6 verfügbar, für VB 5 benötigen Sie einen Ersatz, siehe "Teile-Haberschaft"). Die einzelnen Paar-Strings teilen wir wiederum mit der Split-Funktion in Schlüssel und Wert. Die Teile fügen wir nun in das Array nSettings ein und setzen dieses zum Schluss als Rückgabewert der Funktion.
Wenn wir schon einmal dabei sind, können wir auch gleich eine Variante anlegen, die eine Collection zurückgibt. Wie bei einer Collection üblich, können Sie darin auf die einzelnen Werte des Abschnitts über den Schlüssel zugreifen. Die Funktion GetAllSettingsCollection ähnelt intern der vorangegangenen Funktion GetAllSettings und unterscheidet sich nur im Einlesen der Schlüssel-/Wert-Paare in die Collection.
Public Function GetAllSettingsCollection(AppName As String, _
Section As String, Optional ByVal FromIniFile As Boolean) _
As Collection
Dim nBuffer As String
Dim nRet As Long
Dim nSettings As Variant
Dim nCollection As Collection
Dim l As Long
Dim nItem() As String
Const kBufferSize = 32767
If FromIniFile Then
nBuffer = Space$(kBufferSize)
nRet = _
GetPrivateProfileSection(Section, nBuffer, kBufferSize, AppName)
nSettings = Split(Left$(nBuffer, nRet), Chr$(0))
Else
nSettings = VBA.GetAllSettings(AppName, Section)
End If
Set nCollection = New Collection
With nCollection
For l = 0 To UBound(nSettings)
If Len(nSettings(l)) Then
If Left$(nSettings(l), 1) <> ";" Then
nItem = Split(nSettings(l), "=")
.Add nItem(1), nItem(0)
End If
End If
Next 'l
End With
Set GetAllSettingsCollection = nCollection
End Function
Schließlich gibt es noch die Anweisung DeleteSetting, die im VB-Original einen Schlüssel, Abschnitt oder Anwendungseintrag (AppName) aus der Registrierung entfernt. Die entsprechende Umsetzung für INI-Dateien erlaubt ebenfalls das Löschen von Abschnitten und Schlüssel-/Wert-Paaren aus einer INI-Datei, aber auch das Löschen der ganzen Datei selbst. Letzteres übernimmt ein einfacher Aufruf der Kill-Anweisung. Zum Löschen eines Abschnitts oder eines Schlüssels (entsprechend dem Fehlen des jeweiligen Parameters) werden typsichere Deklarationsvarianten der API-Funktion WritePrivateProfileString aufgerufen (DeletePrivateProfileSection und DeletePrivateProfileKey), die einen Nullzeiger anstelle eines Strings übergeben. Dies ist notwendig, da die bloß Übergabe von leeren Strings an WritePrivateProfileString den Abschnitt bzw. den Schlüssel nicht entfernen würde.
Private Declare Function DeletePrivateProfileSection Lib "kernel32" _
Alias "WritePrivateProfileStringA" (ByVal Section As String, _
ByVal NoKey As Long, ByVal NoSetting As Long, _
ByVal FileName As String) As Long
Private Declare Function DeletePrivateProfileKey Lib "kernel32" _
Alias "WritePrivateProfileStringA" (ByVal Section As String, _
ByVal Key As String, ByVal Setting As Long, _
ByVal FileName As String) As Long
Public Sub DeleteSetting(AppName As String, Optional Section As String, _
Optional Key As String, Optional ByVal IniFile As Boolean, _
Optional ByVal IgnoreError As Boolean = True)
Dim nRet As Long
If IniFile Then
If Len(Section) = 0 Then
If IgnoreError Then
On Error Resume Next
End If
Kill AppName
ElseIf Len(Key) = 0 Then
nRet = DeletePrivateProfileSection(Section, 0, 0, AppName)
Else
nRet = DeletePrivateProfileKey(Section, Key, 0, AppName)
End If
If Not IgnoreError Then
If nRet = 0 Then
Err.Raise Err.LastDllError, "DeleteSetting [INI-File]", _
"System Error Code"
End If
End If
Else
If IgnoreError Then
On Error Resume Next
End If
VBA.DeleteSetting AppName, Section, Key
End If
End Sub
|