|
Haben Sie schon einmal versucht, einen wirklich langen Text mit SaveSetting in den "VB and VBA Program Settings" in der Windows-Registrierung abzulegen und beim nächsten Programmstart wieder einzulesen? Bei langen Texten ist unter Windows 2000 bei immerhin einem halben Megabyte Schluss - was darüber hinaus geht, wird abgeschnitten. Oder haben sie das gleiche schon einmal mit einem Bild-Objekt versucht? Da werden Sie noch nicht einmal soweit kommen, dass da irgendetwas verstümmelt werden würde - die SaveSetting-Methode nimmt erst gar nichts an, was sich nicht prinzipiell als String darstellen lässt. Denn selbst nummerische Werte werden von ihr in der Registrierung als Strings abgelegt.
Natürlich könnten Sie in solchen Fällen auf "richtige" API-Funktionen ausweichen, um beliebige Daten und nahezu beliebige Datenmengen in der Registrierung ablegen zu können. Doch falls wenn Sie sich die Details zum nicht gerade einfachen Umgang mit diesen API-Funktionen befassen, und auch nicht auf eine Komponente, die diese Aufgabe vereinfacht, zurückgreifen möchten, können Sie die Aufgabe durchaus auch mit Visual Basic-Bordmitteln lösen.
Das Ablegen von langen Text-Strings ist der erste und einfachere Schritt. Sie zerlegen einfach den abzulegenden Mammut-String in verträglich kurze Stücke und legen diese einzeln ab. Sie können dazu den ursprünglich beabsichtigten Schlüssel verwenden, den Sie lediglich um die Nummer des entsprechenden Teilstücks ergänzen. Genauer gesagt: Der Schlüssel wird zu einer weiteren Hierarchiestufe, und die Nummern der Teilstücke werden zu den Keys dieser neuen Ebene (Falls Sie noch nicht gewusst haben sollten, dass Sie ohne weiteres eine tiefere Gliederung erhalten können, finden Sie mehr dazu unter: "Verzweigte Settings"). Diese Zusammenfassung unter einem neuen Schlüssel dient in erster Linie dazu, das Löschen einer solchermaßen gesplitteten Ablage zu vereinfachen.
Private Const kMaxLen = 8192 ' maximal 524288
Public Sub SaveSettingLongString(AppName As String, _
Section As String, Key As String, LongString As String, _
Optional ByVal MaxLen As Long = kMaxLen)
Dim nStr As String
Dim l As Long
Dim nPart As Integer
Dim nPartStr As String
On Error Resume Next
DeleteSetting AppName, Section & "\" & Key
On Error GoTo 0
l = 1
Do While l < Len(LongString)
nPart = nPart + 1
SaveSetting AppName, Section & "\" & Key, nPart, Mid$(LongString, _
l, MaxLen)
l = l + MaxLen
Loop
End Sub
Der Rückweg sieht dementsprechend aus:
Public Function GetSettingsLongString(AppName As String, _
Section As String, Key As String) As String
Dim nParts As Variant
Dim nPart As Integer
Dim nStr As String
nParts = GetAllSettings(AppName, Section & "\" & Key)
If Not IsEmpty(nParts) Then
For nPart = LBound(nParts, 1) To UBound(nParts, 1)
nStr = nStr & nParts(nPart, 1)
Next nPart
End If
GetSettingsLongString = nStr
End Function
Die zweite zu lösende Aufgabe lautete, ein Bild-Objekt in der Registrierung abzulegen. Wie ich bereits oben erwähnt hatte, können Sie mit den VB-Methoden nur Strings bewältigen. Es stellt sich also die Frage, wie Sie ein Bild-Objekt in einen String umwandeln können (Die damit verbundene Implikation, dass ein Bild recht groß sein kann, und dass nach der Umwandlung womöglich ein überlanger String vorliegen könnte, haben wir ja bereits erledigt). Vielleicht fällt Ihnen jetzt ein, dass man in Visual Basic ohne weiteres ein Byte-Array einem String zuweisen kann. Wenn Sie nun noch die Teilaufgabe lösen, ein Bild-Objekt in die Form eines Byte-Arrays zu überführen, wäre damit offensichtlich die Gesamtaufgabe gelöst.
Gehen wir einmal davon aus, die Lösung dieser Teilaufgabe gelingt Ihnen (mit Hilfe eines PropertyBag-Objekts spätestens nach der Lektüre dieses Artikels: "Bilder zu Bytes"), werden Sie allerdings eine Überraschung erleben. Sie werden den gewonnenen String zwar scheinbar mit SaveSetting ablegen können, doch GetSetting wird Ihnen wieder nur einen verstümmelten String zurück geben. Allerdings liegt das diesmal nicht an der Längenbegrenzung eines Registrierungseintrags. Der Stolperstein ist vielmehr, dass in der Byte-Array-Repräsentation eines Bild-Objekts alle Byte-Werte von 0 bis 255 vorkommen können. Ein Byte mit dem Wert 0 würde nach der Zuweisung des Byte-Arrays an einen String in diesem als 0-Zeichen (wie Chr$(0)) auftreten. Da im Hintergrund von SaveSetting wieder die Eingangs erwähnten API-Funktionen werkeln, und diese in typischer Manier der Programmiersprache C das Ende eines Strings anhand von solchen 0-Zeichen zu bestimmen gewohnt sind, kappen sie den übergebenen Byte-String an der Stelle des ersten Auftretens eines 0-Zeichens. Wir benötigen also eine andere Umsetzung eines Byte-Arrays in die String-Form.
Eine einfache Darstellung wäre die Darstellung der Zeichen eines Strings in hexadezimaler Form. Das bedeutet, dass der Ascii-Wert eines Zeichens über die Hex$-Funktion von VB in ein Paar aus zwei Zeichen umgesetzt wird (gegebenenfalls mit führender 0). Da der String einer solchermaßen umgesetzten Byte-Folge nur aus den Zeichen für die Ziffern 0 bis 9 und den Hexadezimal-"Ziffern" A bis F besteht, kann er nun problemlos von SaveSetting bzw. unserer Prozedur SaveSettingLongString in der Registrierung abgelegt werden.
Die Umwandlung zwischen Byte-Array und String und retour erledigen die beiden Funktionen BytesToHexString und HexStringToBytes:
Public Function BytesToHexString(Bytes() As Byte) As String
Dim l As Long
Dim nStr As String
Dim nHex As String
nStr = Space$((UBound(Bytes) + 1) * 2)
For l = 0 To UBound(Bytes)
nHex = Hex$(Bytes(l))
If Len(nHex) = 1 Then
Mid$(nStr, (l * 2) + 1, 2) = "0" & nHex
Else
Mid$(nStr, (l * 2) + 1, 2) = nHex
End If
Next 'l
BytesToHexString = nStr
End Function
Public Function HexStringToBytes(Str As String) As Variant
Dim l As Long
Dim nBytes() As Byte
ReDim nBytes(0 To (Len(Str) - 1) \ 2)
For l = 1 To Len(Str) Step 2
nBytes((l - 1) \ 2) = Val("&H" & Mid$(Str, l, 2))
Next 'l
HexStringToBytes = nBytes
End Function
Da wir uns oben schon die praktischen Varianten von SaveSettings und GetSettings für überlange Strings haben, gönnen wir uns nun noch den Komfort entsprechender Varianten auch für PropertyBag-Objekte, in die Sie Ihre Bild-Objekte verpackt haben:
Public Sub SaveSettingsPropertyBag(AppName As String, _
Section As String, Key As String, PropBag As PropertyBag, _
Optional ByVal MaxLen As Long = kMaxLen)
SaveSettingLongString AppName, Section, Key, _
BytesToHexString(PropBag.Contents), MaxLen
End Sub
Public Function GetSettingsPropertyBag(AppName As String, _
Section As String, Key As String) As PropertyBag
Set GetSettingsPropertyBag = New PropertyBag
On Error Resume Next
GetSettingsPropertyBag.Contents = _
HexStringToBytes(GetSettingsLongString(AppName, Section, Key))
End Function
Und für den Fall, dass es Ihnen in den Sinn kommen sollte, auch ganz banal anderweitig vorhandene Byte-Arrays ablegen zu wollen, können Sie dazu die beiden folgenden Varianten verwenden:
Public Sub SaveSettingsByteArray(AppName As String, _
Section As String, Key As String, Bytes() As Byte, _
Optional ByVal MaxLen As Long = kMaxLen)
SaveSettingLongString AppName, Section, Key, _
BytesToHexString(Bytes), MaxLen
End Sub
Public Function GetSettingsByteArray(AppName As String, _
Section As String, Key As String) As Variant
GetSettingsByteArray = _
HexStringToBytes(GetSettingsLongString(AppName, Section, Key))
End Function
Und hier sehen Sie schließlich noch ein kleines Beispiel zum Ablegen und Einlesen eines Bild-Objekts - etwa eines Hintergrundbildes eines Forms, das der Anwender in Ihrem Programm beliebig ändern könnte und beim nächsten Programmstart sich gerne wieder vorfinden würde:
Private Sub Form_Unload()
Dim nPropBag As PropertyBag
Set nPropBag = New PropertyBag
nPropBag.WriteProperties "Hintergrund", Me.Picture
SaveSettingsPropertyBag _
"MeineAnwendung", "Bilder", "Hintergrund", nPropBag
End Sub
Private Sub Form_Load()
Dim nPropBag As PropertyBag
Set nPropBag = GetSettingsPropertyBag("MeineAnwendung", _
"Bilder", "Hintergrund")
Set Me.Picture = nPropBag.ReadProperties("Hintergrund")
End Sub
Kleine Schlussbemerkung: Sie können natürlich auch mehrere Bilder gleichzeitig in ein einziges PropertyBag-Objekt verpacken. Und dazu auch alle anderen Daten, die ein PropertyBag aufzunehmen in der Lage ist. Der einzige kleine Haken ist allerdings, dass die Möglichkeit, eigene Instanzen eines PropertyBag-Objekts anzulegen, erst ab Visual Basic 6 zur Verfügung steht. In Visual Basic 5 können Sie jedoch immerhin die Ablage überlanger Strings nutzen.
|