Icons in Ribbon, Kontextmenü, Formular und TreeView

Seit Jahren gibt es im Web einige Module, die das Handling von Bilddateien als Icons in Ribbon, Kontextmenüs, TreeViews oder als Formular-Icon erlauben. Diese sind meist umfangreich und für Laien kaum verständlich. Wir haben die verwendeten Techniken einmal ein wenig einfacher gestaltet, sodass man nicht mehr so viel Code benötigt – und vor allem weniger API-Funktionen. In diesem Artikel lernen Sie, wie Sie Ribbon-Einträge und Kontextmenü-Befehle mit Icons versehen. Als Bonus gibt eine hilfreiche Funktion, mit der wir schnell viele Icons in die Access-Datenbank übertragen können, statt diese Schritt-für-Schritt einzulesen.

Theorie zu Bilddateien für Icons und Ribbons

Microsoft Access speichert seit der Version 2010 Bilder, die man aus dem Dateisystem auswählt und Steuerelementen wie dem Bildsteuerelement oder der Schaltfläche zuordnet, in einer Systemtabelle der Datenbank namens MSysResources.

Nachdem wir einem Formular im Entwurf über den Ribbonbefehl Bild einfügen ein Bild hinzugefügt haben, ist dieses anschließend ständig verfügbar (siehe Bild 1).

Bilder sind nach dem Hinzufügen ständig in der aktuellen Datenbank verfügbar.

Bild 1: Bilder sind nach dem Hinzufügen ständig in der aktuellen Datenbank verfügbar.

Die Bilddateien werden intern versteckt gespeichert, sondern in der bereits erwähnten Tabelle MSysResources.

Diese sieht nach dem Hinzufügen einiger Bilder wie in Bild 2 aus.

Tabelle zum Speichern der Bilder in der aktuellen Datenbank

Bild 2: Tabelle zum Speichern der Bilder in der aktuellen Datenbank

All diese können wir allerdings auch anderweitig nutzen, wenn sie sich einmal in der Datenbank befinden – zum Beispiel, indem wir sie per VBA auslesen und in Ribbons oder Kontextmenüs anzeigen.

Vorbereitung: Icons in der Datenbank speichern

Der Dialog zum Hinzufügen von Bildern zur Datenbank über den Befehl Bild einfügen hat einen Nachteil, der bereits aus der Beschriftung dieses Befehls ersichtlich wird – man kann immer nur ein Bild pro Vorgang hinzufügen. Wenn man einen Satz von Bildern hat, die man immer wieder benötigt, wird das schnell aufwendig.

Wir stellen also in diesem Beitrag eine Funktion vor, mit der wir gleich mehrere Bilder im Dateidialog auswählen und zur Datenbank hinzufügen können.

Bilder in Ribbon und Kontextmenü

Während wir die Bilder aus der Tabelle MSysResources zu Bild-Steuerelementen oder Schaltflächen hinzufügen können, indem wir diese einfach für die Eigenschaft Bild auswählen (siehe Bild 3), ist das bei den Schaltflächen und anderen Elementen im oder in Schaltflächen im Kontextmenüs nicht so einfach möglich. Diese erwarten ein per VBA zugewiesenes Element des Typs StdPicture.

Die Bilder stehen in der Bild-Eigenschaft zur Verfügung

Bild 3: Die Bilder stehen in der Bild-Eigenschaft zur Verfügung

Ein Bild aus der Tabelle MSysResources auszuwählen und in ein StdPicture-Element umzuwandeln ist kein Hexenwerk, aber dazu sind einige Codezeilen nötig und eine Reihe API-Funktionen. Im zweiten Abschnitt dieses Beitrags werden wir eine Vorgehensweise vorstellen, die wesentlich kompakter als die üblicherweise verwendeten und auch in diesem Magazin eingeführten Techniken sind.

Außerdem zeigen wir, wie diese Elemente dann den entsprechenden Steuerelementen zugewiesen werden.

Schnelles Einlesen von vielen Icons

Zuerst schauen wir uns die Funktion zum schnellen Einlesen von mehreren Bilddateien an.

Diese erfordert zunächst eine Funktion, mit der wir die einzulesenden Dateien per Dateidialog auswählen können. Außerdem benötigen wir eine Routine, welche die ausgewählten Dateien in das Anlagefeld der Tabelle MSysResources schreibt. Diese beiden Routinen führen wir in einer aufrufenden Prozedur namens amvFileToResources zusammen.

Die Funktion zum Anzeigen des Dateidialogs basiert auf dem FileDialog-Klasse der Office-Bibliothek, auf die wir noch einen Verweis setzen müssen (siehe Bild 4).

Hinzufügen des Verweises auf die Office-Bibliothek

Bild 4: Hinzufügen des Verweises auf die Office-Bibliothek

Diese benötigen wir auch später noch, wenn wir ein Kontextmenü mit Icons erstellen wollen.

Steuerung des Einlesevorgangs der Bilddateien

Die Prozedur amvFileToResources steuert unseren Vorgang:

Public Sub amvFilesToResources()
     Dim strPaths() As String
     Dim strPathlist As String
     Dim i As Integer
         
     strPathlist = ChooseFiles
     strPaths() = Split(strPathlist, "|")
     For i = LBound(strPaths) To UBound(strPaths)
         Debug.Print strPaths(i)
         AddImageToMSysResources strPaths(i)
     Next i
End Sub

Sie ruft zunächst die Funktion ChooseFiles auf, die eine Zeichenkette mit den durch das Pipe-Zeichen (|) getrennten einzelnen Pfaden der einzulesenden Dateien zurückgibt.

Diese Liste speichern wir in der Variablen strPathlist. Den Inhalt dieser Variablen teilen wir mit der Split-Funktion in ein Array auf, das wir in der Variablen strPaths() speichern.

Die Elemente dieses Arrays durchlaufen wir nachfolgend in einer For…Next-Schleife. In dieser Schleife rufen wir für jedes Element einmal die Funktion AddImageToMSysResources auf, um die jeweilige Datei in einen neuen Datensatz der Tabelle MSysResources zu schreiben.

Die Funktion ChooseFiles

Die Funktion zum Auswählen der Dateien nutzt den Dateiauswahldialog der Office-Bibliothek, um dem Benutzer eine flexible Mehrfachauswahl von Dateien zu ermöglichen, und gibt die Pfade der ausgewählten Dateien als durch das Pipe-Zeichen getrennte Zeichenkette zurück (siehe Listing 1). Gleichzeitig merkt sich die Funktion den beim letzten Vorgang verwendeten Ordnerpfad und schlägt diesen beim nächsten Aufruf erneut vor, was eine erhebliche Erleichterung bei wiederholter Arbeit mit denselben Ordnern darstellt.Zu Beginn der Funktion werden die nötigen Variablen deklariert. Eine dieser Variablen speichert das Initialverzeichnis für den Dateiauswahldialog. Dieser Pfad wird mithilfe der Funktion GetAppSetting a(siehe Modul mdlRegistry) aus dem Bereich der Registry für Anwendungseinstellungen ausgelesen. Ist dort kein Pfad hinterlegt, etwa weil die Funktion zum ersten Mal verwendet wird oder die Einstellung gelöscht wurde, verwendet die Funktion standardmäßig das Verzeichnis der aktuellen Datenbankdatei. Das bedeutet, dass der Benutzer beim ersten Aufruf im Ordner der Datenbank zu arbeiten beginnt, bei weiteren Aufrufen jedoch direkt im zuletzt verwendeten Verzeichnis landet.

Public Function amvChooseFiles()
     Dim objFileDialog As Office.FileDialog
     Dim strTemp As String
     Dim varFilename As Variant
     Dim strInitialFilename As String
     strInitialFilename = amvGetAppSetting("InitialFilename")
     If Len(strInitialFilename) = 0 Then
         strInitialFilename = CurrentProject.Path & "\"
     End If
     Set objFileDialog = Application.FileDialog(msoFileDialogOpen)
     With objFileDialog
         .Title = "Bilddateien auswählen"
         .ButtonName = "Auswählen"
         .AllowMultiSelect = True
         .InitialFileName = strInitialFilename
         .Filters.Clear
         .Filters.Add "PNG-Dateien", "*.png"
         .Filters.Add "ICO-Dateien (.ico)", "*.ico"
         If .Show = True Then
             If Not .SelectedItems.Count = 0 Then
                 strInitialFilename = .SelectedItems(1)
                 strInitialFilename = Left(strInitialFilename, InStrRev(strInitialFilename, "\"))
                 For Each varFilename In .SelectedItems
                     strTemp = strTemp & "|" & varFilename
                 Next varFilename
                 If Not Len(strTemp) = 0 Then
                     strTemp = Mid(strTemp, 2)
                 End If
             End If
         End If
     End With
     amvSaveAppSetting "InitialFilename", strInitialFilename
     amvChooseFiles = strTemp
End Function

Listing 1: Ermitteln der einzulesenden Bilddateien per Dateidialog

Anschließend wird ein Dialogobjekt vom Typ FileDialog mit dem Typ msoFileDialogOpen zum Auswählen von Dateien erzeugt. Der Dialog wird so konfiguriert, dass mehrere Dateien auf einmal ausgewählt werden können. Er erhält einen beschreibenden Titel, einen angepassten Beschriftungstext für die Auswahlschaltfläche sowie zwei Filtereinträge: einen für PNG-Dateien und einen für ICO-Dateien.

Zeigt der Benutzer nun den Dialog an und bestätigt die Auswahl, wird überprüft, ob überhaupt Dateien ausgewählt wurden. Ist dies der Fall, wird aus dem Pfad der ersten Datei der zugehörige Ordner extrahiert. Dieser Ordner wird später als neue Anwendungseinstellung gespeichert, damit er beim nächsten Aufruf der Funktion wieder als Ausgangspunkt dient. Danach werden alle ausgewählten Dateipfade nacheinander in einer Zeichenkette gesammelt. Zwischen den einzelnen Dateipfaden wird das Pipe-Zeichen als Trenner eingefügt. Abschließend wird der führende Trenner entfernt, sodass die Rückgabe sauber formatiert ist.

Die Funktion speichert dann den aktualisierten Startordner mithilfe der Prozedur SaveAppSetting, ebenfalls im Modul mdlRegistry zu finden, und gibt schließlich die Zeichenkette mit den Pfaden der ausgewählten Dateien zurück.

Prozedur zum Speichern der Bilder in der Tabelle MSysResources

Nach der Auswahl folgt die Prozedur AddImageToMSysResources. Diese deklariert ein Database-Objekt, zwei Recordsets für die Tabelle MSysResources und die darin enthaltene interne Tabelle zum Speichern der Attachments und ein Field2-Objekt zum Laden der Dateien in das Zielfeld (siehe Listing 2).

Public Sub amvAddImageToMSysResources(ByVal strPath As String)
     Dim db As DAO.Database
     Dim rstResources As DAO.Recordset
     Dim rstAttachments As DAO.Recordset2
     Dim fldFileData As DAO.Field2
     Dim strExtension As String
     Dim strFilename As String
     Dim strFilenameWithoutExtension As String
     strExtension = Mid(strPath, InStrRev(strPath, ".") + 1)
     strFilename = Mid(strPath, InStrRev(strPath, "\") + 1)
     strFilenameWithoutExtension = Left(strFilename, InStrRev(strFilename, ".") - 1)
     Set db = CurrentDb
     Set rstResources = db.OpenRecordset("SELECT * FROM MSysResources", dbOpenDynaset)
     rstResources.AddNew
     
     rstResources!Extension = strExtension
     rstResources!Name = strFilenameWithoutExtension
     rstResources!Type = "img"
     
     Set rstAttachments = rstResources.Fields("Data").Value
     rstAttachments.AddNew
     Set fldFileData = rstAttachments.Fields("FileData")
     fldFileData.LoadFromFile strPath
     rstAttachments.FileName = strFilename
     rstAttachments.Update
     rstAttachments.Close
     Set rstAttachments = Nothing
     
     rstResources.Update
     rstResources.Close
     Set rstResources = Nothing
     Set db = Nothing
End Sub

Listing 2: Die Prozedur AddImageToMSysResources

Die Prozedur erwartet den Pfad der einzulesenden Datei als Parameter. Sie ermittelt zunächst die Dateiendung und den Dateinamen sowie den Dateinamen ohne Dateiendung und trägt diese Daten in die Variablen strExtension, strFilename und strFilenameWithoutExtensions ein.

Dann erstellt sie eine Referenz zum aktuellen Database-Objekt und öffnet darüber die Tabelle MSysResources als Recordset.

Hier fügt sie mit AddNew einen neuen Datensatz hinzu und trägt die Dateiendung in das Feld Extension sowie den Dateinamen ohne Dateiendung in das Feld Name ein. Als Type wird immer img eingetragen.

Nun folgt der spannende Teil. Das zweite Recordset wird mit dem Wert Value des Feldes Data der Tabelle MSysResources gefüllt, also mit der internen Tabelle zum Speichern der Attachments.

Auch hier fügen wir mit AddNew einen neuen Datensatz ein und referenzieren dann das Feld FileData mit der Variablen fldFileData. Die hier verwendete Field2-Klasse liefert gegenüber der herkömmlichen Field-Klasse noch die Methode LoadFromFile, die das Einlesen einer Datei in das Feld ermöglicht. Diese nutzen wir, um die angegebene Datei in das Anlagefeld zu laden. Zusätzlich stellen wir die Eigenschaft Filename auf den Dateinamen ein, diesmal mit Dateierweiterung, und speichern den neu hinzugefügten Datensatz der internen Tabelle mit der Update-Methode.

Die gleiche Methode rufen wir auch noch für das übergeordnete Recordset auf und schließen dann die geöffneten Objekte und geben die Objektvariablen frei.

Vorbereiten der Tabelle MSysResources mit einigen Bilddateien

Rufen wir die Prozedur amvFileToResources nun auf, erscheint der Dateidialog, mit dem wir die gewünschten Bilddateien auswählen können. Die ausgewählten Dateien werden ohne Rücksicht auf eventuelle Dubletten in die Tabelle MSysResources geschrieben.

Nachdem wir dies erledigt haben, sind einige Dateien in dieser Tabelle gelandet, die wir für die folgenden Techniken als Icons für das Ribbon und für Kontextmenüs nutzen können.

Bilder im Ribbon anzeigen

Wenn wir Bilder im Ribbon anzeigen wollen, benötigen wir zweierlei:

  • Ein Element im Ribbon, dessen image-Attribut mit dem Namen des zu verwendenden Bildes aus der Tabelle MSysResources gefüllt ist. Dabei verwenden wir den Namen ohne Dateiendung, so wie er im Feld Name der Tabelle eingetragen ist.
  • Außerdem benötigen wir im Element customUI das Attribut loadImages, das den Namen der Funktion enthält, das beim Anzeigen der jeweiligen Elemente zum Einlesen der Bilder genutzt werden soll.

Eine der einfachsten möglichen Ribbondefinitionen für diesen Zweck haben wir bereits in die für diesen Fall vorbereitete Tabelle USysRibbons eingetragen (siehe Bild 5). Das Attribut des button-Elements enthält den Wert apple, was dazu führt, dass ein Bild namens apple angezeigt werden soll. Das Attribut loadImage ist mit dem Wert loadImage ausgestattet, was bedeutet, dass wir eine gleichnamige Funktion zum Einlesen der Bilder hinterlegen müssen.

Einfachste mögliche Ribbondefinition zum Anzeigen eines benutzerdefinierten Bildes

Bild 5: Einfachste mögliche Ribbondefinition zum Anzeigen eines benutzerdefinierten Bildes

Funktionen für die Automation von Ribbons hinterlegen wir traditionell in einem Modul namens mdlRibbons. In diesem Fall fügen wir dort die folgende Funktion ein:

Public Sub loadImage(control, ByRef image)
     Set image = GetImageFromResourcesByName(control)
End Sub

Sie ruft lediglich eine weitere Funktion namens amvGetImageFromResourcesByName auf, die den Wert des Parameters control übergeben bekommt. Diese Funktion liefert das Bild in Form einer Variable des Typs StdPicture zurück, die schließlich an die Ribbondefinition zurückgegeben werden kann.

Im Gegensatz zu Prozeduren, die zum Beispiel beim Anklicken eines Ribbonbuttons ausgelöst werden, liefert control hier kein Objekt des Typs IRibbonControl, sondern lediglich den Wert des Attributs image des jeweils mit einer Bilddatei zu füllenden Steuerelements.

ID der einzufügenden Bilddatei ermitteln

Die Funktion amvGetImageFromResources ist eine Wrapperfunktion, die zu dem übergebenen Bildnamen die ID des entsprechenden Datensatzes aus der Tabelle MSysResources ermittelt (siehe Listing 3).

Public Function amvGetImageFromResourcesByName(strName As Variant) As StdPicture
     Dim db As DAO.Database
     Dim rst As DAO.Recordset
     Dim lngID As Long
     
     Set db = CurrentDb
     Set rst = db.OpenRecordset("SELECT ID FROM MSysResources WHERE Name = ''" & strName & "''", dbOpenSnapshot)
     
     Select Case rst.RecordCount
         Case 1
             lngID = rst.Fields(0)
             Set amvGetImageFromResourcesByName = amvGetImageFromResources(lngID)
         Case 0
             MsgBox "Bild ''" & strName & "'' nicht in der Tabelle MSysResources gefunden."
         Case Else
             MsgBox "Mehr als ein Bild namens ''" & strName & "'' in der Tabelle MSysResources vorhanden."
     End Select
End Function

Listing 3: Die Funktion GetImageFromResources

Sie ermittelt per Recordset alle Datensätze, deren Bild den entsprechenden Namen hat.

Die weitere Vorgehensweise hängt davon ab, wie viele Datensätze mit diesem Bildnamen gefunden wurden:

  • Im Falle eines Bildes kann zuverlässig das richtige Bild gewählt werden – dann ruft die Funktion eine weitere Funktion namens amvArrayToPictureFromResources auf und lädt so das Bild in das Ribbon (siehe Listing 4).
  • Public Function amvGetImageFromResources(lngID As Long) As StdPicture
         Dim bytData() As Byte
         
         On Error GoTo hErr
         
         bytData() = amvAttachmentToByteArray(lngID)
         Set amvGetImageFromResources = amvArrayToStdPicture(bytData)
    Exit Function
    hErr:
         Err.Clear
    End Function

    Listing 4: Die Funktion amvGetImageFromResources

  • Wird kein Bild gefunden, erscheint eine entsprechende Meldung.
  • Wenn mehr als ein Bild gefunden wird, wird ebenfalls eine passende Meldung angezeigt.

Ermitteln des StdPicture-Objekts für die angegebene Ressource

Die Funktion zum Einlesen einer Ressource in eine Objektvariable des Typs StdPicture nimmt die ID des zu verwendenden Datensatzes der Tabelle MSysResources entgegen und soll schließlich das StdPicture-Objekt zurückliefern.

Dies geschieht in zwei Schritten. Der erste ruft die Funktion amvAttachmentToByteArray auf, die das Attachment zunächst in ein Byte-Array einliest. Das Ergebnis dieser Funktion, das zwischenzeitlich in der Variablen bytData landet, wird dann in der Funktion amvArrayToStdPicture weiterverarbeitet. Diese liefert schließlich das StdPicture-Objekt zurück.

Funktion zum Erzeugen eines Byte-Arrays aus einem Attachment

Die Funktion amvAttachmentToByteArray aus Listing 5 nimmt den Primärschlüsselwert der Ressource aus der Tabelle MSysResources entgegen und liefert ein Byte-Array mit dem Inhalt des Anlagefeldes zurück.

Public Function amvAttachmentToByteArray(lngID As Long) As Byte()
     Dim db As DAO.Database
     Dim rst As DAO.Recordset
     Dim bytAttach() As Byte
     Dim bytAttachData() As Byte
     Dim lngOffset As Long
On Error GoTo hErr
     
     Set db = CurrentDb
     Set rst = db.OpenRecordset("SELECT Data.FileData FROM MSysResources WHERE id = " & lngID, dbOpenSnapshot)
     If Not rst.EOF Then
         bytAttachData = rst.Fields(0)
         lngOffset = bytAttachData(0)
         ReDim bytAttach(UBound(bytAttachData) - lngOffset)
         amvCopyMemory bytAttach(0), bytAttachData(lngOffset), UBound(bytAttach)
         amvAttachmentToByteArray = bytAttach
     
         Erase bytAttachData
         Erase bytAttach
     Else
         MsgBox "Attachment mit der ID ''" & lngID & "'' konnte nicht in MSysResources gefunden werden."
     End If
     
     Exit Function
hErr:
     Err.Clear
End Function

Listing 5: Die Funktion amvAttachmentToByteArray

Die Funktion soll aus dem Eintrag der Tabelle MSysResources den tatsächlichen Inhalt des gespeicherten Bildes als reines Byte-Array zu extrahieren. Der besondere Fokus liegt dabei auf der Tatsache, dass Access Anlagen intern mit einem zusätzlichen Datenkopf versieht.

Dieser Kopf enthält Verwaltungsinformationen, die beim späteren Verwenden der Daten (zum Beispiel in einem Bildobjekt oder beim Speichern in eine Datei) nicht benötigt und daher entfernt werden müssen. Die Funktion befreit die gespeicherte Datei also von diesem Overhead und liefert nur den reinen, nutzbaren Inhalt zurück.

Ziel der Funktion ist es, für einen übergebenen Primärschlüsselwert – also einen Verweis auf einen Datensatz in MSysResources – den enthaltenen Anlagendatensatz zu lesen, den tatsächlichen Startpunkt der eigentlichen Binärdaten zu ermitteln, diese dann isoliert in ein neues Byte-Array zu kopieren und schließlich zurückzugeben. Sie eignet sich somit hervorragend als Bindeglied zwischen der internen Speicherung von Ressourcen in Access und einer externen Weiterverwendung, beispielsweise zur Übergabe an Windows-APIs oder wie hier an benutzerdefinierte Ribbon-Schaltflächen, die ein StdPicture-Objekt erwarten.

Zunächst werden zwei Byte-Arrays deklariert: bytAttachmentData, welches den vollständigen Inhalt aus dem Anlagenfeld übernimmt, und bytAttachment, das später den bereinigten, reinen Datenanteil aufnehmen soll.

Eine dritte Variable, lngOffset, speichert die Position im Array, ab der der tatsächliche Inhalt beginnt. Dieser Offset wird durch den Wert des ersten Bytes des Arrays bestimmt – bytAttachmentData(0) -, welches laut interner Speicherstruktur von Access die Länge des Headers angibt. Dieser Trick ist elementar, da er vermeidet, das genaue Format des Anlagendatentyps analysieren zu müssen. Stattdessen wird das erste Byte als Startmarke interpretiert und als Versatz für das Kopieren der Daten genutzt.

Die Abfrage selbst erfolgt über ein Recordset im Snapshot-Modus, um eine möglichst performante und schreibgeschützte Lesung zu gewährleisten. Das SQL-Statement greift auf die Spalte FileData des eingebetteten Anlagenfelds Data zu. Dies ist möglich, da das Anlagenfeld, wie oben bereits erwähnt, intern als komplexer Datentyp mit Unterfeldern gespeichert wird, wobei FileData die eigentlichen Binärdaten enthält.

Nach dem Lesen des vollständigen Datensatzes und dem Ermitteln des Offsets wird das Zielarray bytAttachment mit der Größe des verbleibenden Datenblocks dimensioniert. Der Copy-Vorgang erfolgt über die API-Funktion amvCopyMemory, die wie in Listing 6 neben zwei weiteren, später noch benötigten API-Funktionen im Kopf des Moduls mdlAPI deklariert wird.

Public Declare PtrSafe Sub amvCopyMemory Lib "Kernel32.dll" Alias "RtlMoveMemory" (Destination As Any, _
     Source As Any, ByVal Length As LongPtr)
Public Declare PtrSafe Function amvCreateIconFromResourceEx Lib "user32.dll" Alias "CreateIconFromResourceEx" _
     (presbits As Any, dwResSize As Any, ByVal fIcon As Long, ByVal dwVer As Long, ByVal cxDesired As Long, _
     ByVal cyDesired As Long, ByVal flags As Long) As LongPtr
Public Declare PtrSafe Function amvSendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As LongPtr, _
     ByVal wMsg As Long, ByVal wParam As LongPtr, lParam As Any) As LongPtr

Listing 6: Die für die vorliegende Lösung benötigten API-Funktionen

Die API-Deklarationen haben wir bewusst jeweils mit dem Präfix amv… versehen. Dazu ist es erforderlich, die ursprüngliche API-Funktion mit dem Parameter ALIAS zu versehen. Der Sinn dieser Aktion ist, dass diese Funktionen gegebenenfalls in VBA-Projekte eingefügt werden, die bereits über die eine oder andere dieser API-Funktionen verfügt. Damit diese nicht mühsam abgeglichen werden müssen, haben wir einfach unseren eigenen Satz von API-Funktionen bereitgestellt.

Mit amvCopyMemory werden die reinen Daten ab der Offset-Position aus dem ursprünglichen Array in das neue Array geschrieben – bytegenau und ohne den Headeranteil.

Nach dem erfolgreichen Kopieren wird das bereinigte Array als Ergebnis der Funktion zurückgegeben. Zusätzlich werden die beiden internen Arrays am Ende der Funktion mit Erase freigegeben, um den Arbeitsspeicher zu entlasten.

Tritt während des Prozesses ein Fehler auf, springt der Code in die Fehlerbehandlung, die hier nur aus einem Err.Clear besteht. Damit wird ein eventueller Fehlerzustand gelöscht, ohne dass eine Meldung angezeigt wird. Dies ist in Situationen nützlich, in denen bewusst fehlertolerant gearbeitet werden soll – etwa, wenn der Aufrufer entscheidet, was im Fehlerfall zu tun ist.

Funktion zum Überführen des Byte-Arrays in ein StdPicture-Objekt

Die Funktion amvArrayToStdPicture aus Listing 7 dient der Umwandlung eines Byte-Arrays in ein Objekt des Typs StdPicture.

Public Function amvArrayToStdPicture(bytArray() As Byte) As StdPicture
     On Error GoTo hErr
     With CreateObject("WIA.Vector")
         .BinaryData = bytArray
         Set amvArrayToStdPicture = .Picture
     End With
     Exit Function
hErr:
     Err.Clear
End Function

Listing 7: Funktion zum Umwandeln des Byte-Arrays in ein StdPicture-Objekt

Die Funktion nutzt dafür auf elegante Weise die Windows Image Acquisition-Bibliothek (kurz: WIA), die seit Windows XP Bestandteil des Betriebssystems ist und über COM bereitgestellt wird.

Das zentrale Ziel der Funktion ist es, das Bild aus dem Byte-Array direkt in ein StdPicture-Objekt zu überführen.

Die Funktion erstellt ein neues Objekt auf Basis der WIA-Bibliothek, konkret ein sogenanntes Vector-Objekt. Dieses Objekt dient der Aufnahme von binären Daten und verfügt über eine Eigenschaft BinaryData, der das Byte-Array direkt zugewiesen wird. Intern analysiert die WIA-Bibliothek das Format der Binärdaten – typischerweise PNG, JPG oder BMP – und wandelt diese automatisch in eine Bilddarstellung um, sofern das Format gültig ist.

Sobald das Byte-Array zugewiesen wurde, steht das resultierende Bild über die Eigenschaft Picture als COM-Objekt vom Typ StdPicture zur Verfügung. Dieses Objekt wird dann über Set an die Funktion zurückgegeben.

Die Eleganz dieser Lösung liegt vor allem in der Kombination von Einfachheit und Effektivität: In nur wenigen Codezeilen wird ein häufig komplexer Vorgang – das Interpretieren von Bilddaten und deren Konvertierung in eine COM-kompatible Darstellung – vollständig gelöst. Besonders hervorzuheben ist, dass keinerlei temporäre Dateien oder externe Bibliotheken notwendig sind. Die WIA-Bibliothek ist in der Regel bereits auf jedem Windows-System vorhanden und erfordert lediglich den passenden Verweis, wobei CreateObject in diesem Fall sogar ohne expliziten Verweis in den VBA-Verweiseinstellungen funktioniert.

Testen der Funktionen zum Anzeigen eines Bildes aus MSysResources im Ribbon

Damit haben wir fast alles zusammen. Wir müssen dies nur noch testen, was wir der Einfachheit halber ohne aufwendiges Öffnen und Schließen erledigen wollen.

Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...

den kompletten Artikel im PDF-Format mit Beispieldatenbank

diesen und alle anderen Artikel mit dem Jahresabo

Schreibe einen Kommentar