Home > Artikel > Ausgabe 11/2016 > Outlook-E-Mails importieren

Outlook-E-Mails importieren

  PDF ansehen

  Download PDF und Beispieldatenbank

Über die Automationsschnittstelle von Outlook erhalten Sie Zugriff erhalten Sie Zugriff auf fast alle Aspekte der Anwendung. Das Versenden von E-Mails über das Outlook-Objektmodell aus Access heraus etwa wurde bereits in früheren Beiträgen dargestellt. Hier geht es nun um das Auslesen von E-Mails und deren Import in die Tabellen einer Datenbank. Ein einfaches Formular, welches die Mails zur Anzeige bringt, fehlt ebenfalls nicht.

Beispieldatenbank

Die Beispiele dieses Artikels finden Sie in der Datenbank 1611_OutlookMails.accdb

Die Schnittstelle zu Outlook

Für den Zugriff auf Outlook benötigen Sie einen zusätzlichen Verweis im VBA-Projekt Ihrer Datenbank. Sie markieren im Verweise-Dialog des Menüs Extras den folgenden Eintrag: Microsoft Outlook xx.0 Object Library. Das xx ist die Versionsnummer Ihres Office-Pakets, welche bei Office 2010 etwa 14 lautet. Im Objektkatalog finden Sie anschließend das Objektmodell unter der Rubrik Outlook, in dem die Klasse Application den Ausgangspunkt für alle weiteren Aktionen darstellt.

Vor die Automation, also die Fernsteuerung, von Outlook hat Microsoft allerdings einen Riegel geschoben, da es hier um sicherheitsrelevante Daten gehen kann. In früheren Office-Versionen erschien beim automatisierten Zugriff grundsätzlich der sogenannte Security Guard. Das ist ein kleiner Dialog, der anzeigt, wenn offenbar Zugriff auf die Elemente von Outlook erhalten werden soll. Dies ist zu bestätigen, weil sonst die Schnittstelle erst gar nicht freigeschaltet wird. Außerdem ist dieser Zugriff zeitlich eingeschränkt.

In den neueren Versionen wurde dieses Verhalten geändert. Der Dialog erscheint nur dann, wenn kein funktionstüchtiges Antiviren-Programm installiert ist. Die Existenz eines solchen scheint ausreichend zu sein.

Wie die Seite für den Programmgesteuerten Zugriff im Sicherheitscenter der Optionen von Outlook zeigt (Bild 1), sind sämtliche Auswahlmöglichkeiten deaktiviert, wenn Windows ein akzeptiertes Antiviren-Programm vorfindet. In diesem Fall, von dem wir auch ausgehen, brauchen Sie sich um weiter nicht zu kümmern.

Die Sicherheitseinstellungen von Outlook beim Zugriff über Automation

Bild 1: Die Sicherheitseinstellungen von Outlook beim Zugriff über Automation

Outlook-Application

Alle Elemente von Outlook erreichen Sie über dessen Application-Objekt. Sie erzeugen eine neue Instanz des Objekts, das die Outlook-Anwendung wiederspiegelt, am besten über diese Funktion:

Dim objOL As Outlook.Application

Set objOL = CreateObject( _

"Outlook.Application")

Dabei spielt es keine Rolle, ob Outlook bereits geöffnet ist. Denn Outlook lässt ja auch manuell keine mehrfachen Anwendungsinstanzen zu. CreateObject führt damit bei bereits geöffnetem Outlook zum gleichen Resultat, wie GetObject.

Über die Variable objOL können Sie sich nun daran machen, alle Aspekte der Anwendung zu inspizieren. Möglicherweise benötigen SIe die Variable auch in anderen Prozeduren, und dann macht es Sinn, für sie eine Hilfsfunktion zu erstellen, wie in Listing 1. Auf erneute Instanziierungen der Objektvariablen in Ihren Routinen können Sie nun verzichten, indem Sie einfach die Funktion OLApp abrufen, die ihrerseits auf die im Modulkopf definierte Variable m_OL zugreift.

Public m_OL As Outlook.Application

Function OLApp() As Outlook.Application

     If m_OL Is Nothing Then Set m_OL = CreateObject("Outlook.Application")

     Set OLApp = m_OL

End Function

Listing 1: Genereller Zugriff auf eine Outlook-Instanz über die Hilfsfunktion OLApp

War Outlook noch nicht offen, so startet die Routine eine neue Instanz der Anwendung. Davon bekommen Sie zunächst allerdings nichts mit, denn die Visible-Eigenschaft von Application steht auf False. Lediglich das Outlook-Symbol im Infobereich der Taskleiste weist Sie auf den Start hin.

Nun möchten Sie im Prinzip nicht die Anwendung fernsteuern, sondern E-Mails auslesen. Dazu begeben Sie sich eine Ebene tiefer auf die MAPI-Umgebung, welche für den E-Mail-Verkehr zuständig ist:

Dim objNS As Outlook.NameSpace

Set objNS = OLApp.GetNamespace("MAPI")

Dieser Namespace erlaubt nun die Navigation durch die Ordner und deren Inhalte von Outlook.

Ordner und Unterordner

Wie der Windows-Explorer auch, gestattet Outlook die Einrichtung von beliebig verschachtelten Ordnern und Unterordnern. Um die Analogie weiter zu treiben: Gibt es unter Windows verschiedene Laufwerke, so kann es unter Outlook auch mehrere PST-Dateien geben, die in die Anwendung eingeklinkt wurden und die dann jeweils die oberste Ebene der Ordnerstruktur einnehmen. Unterhalb dieser finden die eigentlichen Verzeichnisse ihren Platz.

Der Zugriff auf die Ordner geschieht über die Folders-Auflistung des Namespace MAPI. Sie kann komfortabel in einer For-Each-Schleife enumeriert werden:

Dim objFolder As Outlook.Folder

For Each objFolder in objNS.Folders

     Debug.Print objFolder.Name

Next objFolder

Als Ergebnis erhalten Sie hier einen Liste der verwendeten PST-Dateien über deren Ordnernamen.

Jeder Folder enthält seinerseits eine Folders-Auflistung für die möglichen Unterordner:

For Each objFolder in objNS.Folders

    Debug.Print objFolder.Name, _

                objFolder.Folders.Count

Next objFolder

Hier wir zusätzlich zum Namen auch die Anzahl an Unterordnern ausgegeben, die die PST-Dateien aufweisen.

Datenmodell

Bevor es an den weiteren programmatischen Zugriff auf die Outlook-Objekte geht, machen wir uns Gedanken über die Tabellenstruktur, in welche die Outlook-Ordner und die E-Mails importiert werden sollen. Am einfachsten ist das anhand des Beziehungsfensters der Beispieldatenbank (siehe Bild 3) erklärt.

Das Datenmodell der Beispieldatenbank enthält nur drei verknüpfte Tabellen

Bild 2: Das Datenmodell der Beispieldatenbank enthält nur drei verknüpfte Tabellen

Die Tabelle tblStores nimmt Informationen zu den PST-Dateien auf. Es macht Sinn, die Hauptordner dieser Dateien in eine eigene Tabelle zu speichern, um diese von den eigentlichen Verzeichnisstrukturen abzugrenzen. Neben dem Primärschlüssel ID (Autowert) steht in FolderName der Name der PST-Datei, der häufig etwa Standard lautet. File speichert den physischen Dateipfad zur PST-Datei, während Path den logischen Verzeichnispfad innerhalb Outlook wiederspiegelt. Bild 2 zeigt einige Beispiele. EntryID ist ein Zeichenausdruck, der den Store, ähnlich wie ein Schlüssel, eindeutig in Outlook kennzeichnet. Auf den ersten Blick sieht diese ID ähnlich aus, wie eine GUID, ist jedoch 48 Zeichen lang. Anhand dieser EntryIDs kann ein bestimmtes Element in Outlook später immer wieder gefunden werden, auch wenn es verschoben wird. Die überschaubare Routine GetStorageFolders in Listing 2 zeigt, wie die Tabelle gefüllt wird.

Die Tabelle tblStores speichert diverse Informationen zu den PST-Dateien, die Outlook verwendet

Bild 3: Die Tabelle tblStores speichert diverse Informationen zu den PST-Dateien, die Outlook verwendet

Zunächst werden über die Execute-Anweisung alle Datensätze aus der Tabelle gelöscht. Da im Datenmodell für die verknüpfte Tabelle tblMailFolders, die die zu den PST-Dateien gehörigen Ordner enthält, referenziell Löschweitergabe eingestellt ist, werden automatisch auch aus dieser alle Daten entfernt. Dasselbe gilt übrigens auch für den Inhalt der weiter verknüpften Tabelle tblMails, die die E-Mail-Informationen speichert.

Dann werden alle Hauptordner das Namespace MAPI in einer For-Each-Schleife durchlaufen. Der Schleifenzähler oFld ist ein Objekt des Typs Outlook.Folder.

Da er im Folgenden mehrmals benötigt wird, ist auf ihn ein With-Block gesetzt. Wichtig ist die If-Bedingung, welche abfragt, ob es sich beim Ordnertyp um einen E-Mail-Container handelt.

Das ist in der Regel dann der Fall, wenn seine Eigenschaft DefaultItemType den Wert olMailItem aufweist. Allerdings garantiert das noch nicht, dass nicht auch andere Elemente in ihm landen könnten – die Ordner sind im Prinzip recht unspezifisch. Trifft die Bedingung zu, so wird ein neuer Datensatz über das Recordset rs in der Tabelle tblStores angelegt.

Der Outlook-Pfad ergibt sich aus der Eigenschaft FolderPath. Auf den Dateipfad kann indessen nur indirekt zugegriffen werden. Das Eigenschaftsobjekt Store des Folders hat die Methode FilePath, die erst die PST-Datei hergibt. Die Tabelle speichert also im Wesentlichen direkt Eigenschaften des Folder-Objekts. Die zweite Tabelle im Modell, tblMailFolders, verweist über das Fremdschlüsselfeld IDStore auf einen Datensatz in der PST-Tabelle tblStores. In der Tabelle werden in flacher Hierarchie sämtliche vorkommenden Outlook-Ordner aufgelistet. Bild 4 zeigt, die das funktioniert.

Die Tabelle tblMailFolders speichert alle Outlook-Ordner aller verwendeten PST-Dateien in rekursiver Anordnung

Bild 4: Die Tabelle tblMailFolders speichert alle Outlook-Ordner aller verwendeten PST-Dateien in rekursiver Anordnung

Im Feld Folder steht jeweils der Name des Ordners, in Path dessen Outlook-Pfad. Darin gleichen diese Informationen denen der Tabelle tblStores. Auch die EntryID zur eindeutigen Identifizierung des Ordners fehlt hier nicht. Zusätzlich gibt es nun aber eine Spalte ParentID, die die Datensätze rekursive voneinander abhängig macht.

Im Beispiel hat der Ordner Minhorst der PST-Datei Standard die Schlüssel-ID 1865. Der Pfad lautet \\Standard\Minhorst. Dieser Ordner enthält mehrere Unterordner, so etwa das Verzeichnis \Bestell. Damit deutlich wird, dass dies ein Unterordner zu einem anderen Datensatz ist, enthält die ParentID eben den Wert 1865. Die Baumstruktur wird somit über die abhängigen Datensätze mit ihren ID-ParentID-Paaren realisiert. Hat ParentID aber den Wert 0, so handelt es sich um ein Root-Verzeichnis.

Die beiden Routinen in Listing 3 demonstrieren, wie der Verzeichnisbaum in die Tabelle kommt. Dazu öffnet GetFolders gleich zwei Recordsets auf die Tabellen tblStores und tblMailFolders. Alle zuvor in tblStores gespeicherten Datensätze werden nacheinander in einer Schleife abgearbeitet und über die Hauptordnernamen (FolderName) jeweils Folder-Objekte in der Variablen oFld erhalten. Über die Methode Items der Folders-Auflistung (oFlds) kann man nämlich entweder über einen numerischen Index oder über den Ordnernamen als Parameter an ein einzelnes Element gelangen. Anschließend ermittelt Count, ob das Verzeichnis Unterordner aufweist.

Sub GetFolders()

     Dim itm As Variant

     Dim oFlds As Outlook.Folders

     Dim oFld As Outlook.Folder

     Dim rsFld As DAO.Recordset

     Dim rsStore As DAO.Recordset

     

     Set rsFld = CurrentDb.OpenRecordset("SELECT * FROM tblMailFolders", dbOpenDynaset)

     Set rsStore = CurrentDb.OpenRecordset("SELECT * FROM tblStores", dbOpenDynaset)

     Set oFlds = OLApp.GetNamespace("MAPI").Folders

     Do While Not rsStore.EOF

         Set oFld = oFlds.Item(rsStore!FolderName.Value)

         If oFld.Folders.Count > 0 Then GetSubFolders oFld.Folders, rsFld, rsStore!ID

         rsStore.MoveNext

     Loop

     rsFld.Close

     rsStore.Close

End Sub

Sub GetSubFolders(oFolders As Outlook.Folders, rs As DAO.Recordset, ID As Long, Optional lParent As Long)

     Dim oFld As Outlook.Folder

     For Each oFld In oFolders

         rs.AddNew

         rs!Folder = oFld.Name

         rs!IDStore = ID

         rs!EntryID = oFld.EntryID

         rs!ParentId = lParent

         rs!Path = Left(oFld.FolderPath, 255)

         rs.Update

         rs.MoveLast

         If oFld.Folders.Count > 0 Then GetSubFolders oFld.Folders, rs, ID, rs!ID.Value

     Next oFld

End Sub

Listing 3: Die beiden Prozeduren ermitteln sämtliche Outlook-EMail-Verzeichnisse

Ist dem so, dann kommt es zum Aufruf der zweiten Routine GetSubFolders, der nun drei Parameter übergeben werden. Das sind die Folders-Eigenschaft dieses Verzeichnisses, das Recordset für die Ordner (rsFld) und außerdem die ID des Datensatzes in tblStore, damit also die ID des Root-Verzeichnisses.

Das wirkt auf den ersten Blick etwas unverständlich, erklärt sich aber schnell, wenn Sie GetSubFolders genauer unter die Lupe nehmen. Diese Prozedur ist nämlich rekursiv, was bedeutet, dass sie sich selbst aufrufen kann. Zunächst aber wird in ihr über das mitgelieferte Recordset, das nun rs heißt, ein neuer Ordnerdatensatz angelegt und auf ähnliche Weise gefüllt, wie in der Routine zu tblStores. Am Schluss schaut die Prozedur abermals über Count nach, ob das Verzeichnis Unterverzeichnisse aufweist. In diesem Fall ruft sie sich selbst wieder mit neuen Parameterwerten auf, um die Unterordner, Unterunterordner, et cetera, abzuarbeiten. Dabei wird nun der letzte optionale Parameter lParent mit dem aktuellen Wert der ID des Datensatzes übergeben, um die Abhängigkeit herzustellen.

Nach der Ausführung dieser Prozeduren sind die beiden Tabellen komplett mit allen Outlook-Ordern befüllt, und nun kann es an den Import der E-Mails gehen.

E-Mails importieren

Als Speicher für die E-Mails dient die dritte Tabelle tblMails im Datenmodell. Ihr Feld IDFolder verweist auf einen Ordnerdatensatz in tblMailFolders, damit deutlich wird, wo sie abgespeichert war. Auch ein Mail-Objekt in Outlook kennt eine EntryID, die das Element eindeutig kennzeichnet. Die weiteren Felder sind mehr oder weniger direkt vom Outlook-MailItem abgeleitet. From speichert den Absender, To ist die Empfängeradresse. CC gibt mögliche weitere Empfänger an, DateTime (Date-Typ)ist das Datum des Erhalts der E-Mail. Subject entspricht dem Betreff, Body dem Inhalt der Mail. Dieses Feld ist als Memo mit Richtext-Modus angelegt, damit HTML-Inhalt unter Umständen direkt angezeigt werden kann. Ob die E-Mail in diesem Format ankam, entscheidet das Boolean-Feld IsHTML. Attachments schließlich betrifft mögliche Anlagen der E-Mail, wobei wir es hier nicht zu kompliziert machen wollten und das Auslesen dieser beiseitelassen. Im Feld wird lediglich die Anzahl der Anlagen gespeichert.

Bild 5 zeigt den Ausschnitt eines Beispiels aus der gefüllten Tabelle, die später als Basis für ein Anzeigeformular frmMails verwendet wird. Zum Anlegen der Datensätze in ihr kommt die Routine GetMails zum Zug, die in Listing 4 abgebildet ist. Auch wenn ihr Umfang anderes vermuten lässt, ist sie nicht sonderlich komplex gestrickt.

Die Tabelle tblMails nimmt sämtliche E-Mails aus allen Outlook-Ordnern auf und speichert deren wichtigste Daten

Bild 5: Die Tabelle tblMails nimmt sämtliche E-Mails aus allen Outlook-Ordnern auf und speichert deren wichtigste Daten

Alle zuvor in tblMailFolders gespeicherten Outlook-Ordner werden über das Recordset rsFld durchlaufen. Allerdings wird eine hier nicht wiedergegebene Abfrage qryStores verwendet, die die Tabellen tblStores und tblMailFolders kombiniert, wobei sie aus letzterer alle Felder zurückgibt und aus der ersten nur das Feld EntryID. Um einen Ordner in Outlook eindeutig über die Funktion GetFolderFromID identifizieren zu können, benötigt man nämlich nicht nur die EntryID eines Ordners, sondern zusätzlich die Store-ID der PST-Datei, die im Feld SEntryID der Abfrage ausgegeben wird. Die Funktion gibt im Erfolgsfall (Test auf Nothing) ein Folder-Objekt zurück, das der Variablen oFld zugewiesen wird.

Anschließend öffnet sich eine For-Each-Schleife auf alle Elemente des Ordners, die in der Auflistungseigenschaft Items enthalten sind. Als Schleifenvariable wird das unspezifische As Object deklarierte oItm benutzt. Manchmal enthält ein Outlook-Ordner andere Elemente, als E-Mails, auch wenn seine Bestimmung ursprünglich anders festgelegt wurde. Man kann eine Outlook-Aufgabe etwa auch in einen E-Mail-Ordner verschieben. Diese würde dann ebenfalls in der Items-Auflistung enthalten sein und könnte nicht einer Variablen des Typs MailItem zugewiesen werden. Deshalb fragen Sie erst den Typ des Elements über dessen Eigenschaft Class ab. Für eine E-Mail muss diese Klassen-ID den Wert olMail aufweisen. Ist dies korrekt, so geht es nach der If-Bedingung weiter und das Elementobjekt oItm kann der Variablen oMail zugewiesen werden – derlei nennt man auch object casting.

Die Eigenschaften des MailItem-Objekts oMail werden nun einem neuen Datensatz der Tabelle tblMails verabreicht, auf die über das Recordset rsMails zugegriffen wird. Das ist relativ unspektakulär, aber einige Besonderheiten müssen berücksichtigt werden.

CC etwa kann eine lange Liste von Empfängern enthalten, die über die 255 für das Feld erlaubte Zeichen hinausgeht. Deshalb schneidet das Left einen zu langen String hier einfach ab. Für DateTime wird das Empfangsdatum ReceivedTime verwendet. Es ist zu erwähnen, dass ein MailItem noch einige weitere Datums- und Zeitangaben enthält, wie etwa das Erstelldatum (CreationTime). Mit der Empfängeradresse ist es etwas anders, als mit CC, da eine E-Mail auch an mehrere An-Empfänger gerichtet worden sein kann. Das spiegelt die Eigenschaft To nicht unbedingt wieder. Stattdessen wird hier die Recipients-Auflistung benutzt, deren erstes Element (Recipients.Item(1)) ausgelesen wird.

Ist die E-Mail im HTML-Format, so steht die Eigenschaft BodyFormat des MailItems auf dem Wert der Konstanten olFormatHTML. In diesem Fall muss die Eigenschaft HTMLBody für den Inhalt verwendet werden, andernfalls die Eigenschaft Body. Der If-Then-Else-Zweig nimmt diese Unterscheidung vor. Das war's im Wesentlichen.

In die Schleifen der Routine sind noch Abbruchbedingungen eingebaut, die Sie optional entfernen könnten. Allerdings dauert es schon einige Zeit, bis alle E-Mails eingelesen wurden, und wenn Sie üppige Postfächer in Outlook haben, kann dies die Datenbank außerdem durchaus sprengen. Deshalb wird in der globalen Variablen maxcnt ein Wert gespeichert, der die maximale Anzahl einzulesender Mails enthält. Die Zählervariable cnt hingegen wird bei jeder E-Mail erhöht. Ist cnt größer, als maxcnt, dann wird die innere Schleife über Exit For verlassen, die äußere über Exit Do. Die Routine bricht an dieser Stelle ab.

Listing 5 zeigt nun, wie die besprochenen Prozeduren nacheinander aufgerufen werden können. Die Zählervariable cnt wird auf Null gesetzt, maxcnt auf maximal 5000 einzulesende E-Mails eingestellt. Dann folgen die drei für den Import verantwortlichen Prozeduraufrufe. Sinnvoll ist es zudem, im Anschluss Outlook wieder zu schließen. Das übernimmt die Methode Quit der globalen Outlook.Application-Variablen m_OL.. Allerdings schließt sich die Anwendung damit grundsätzlich, auch wenn sie schon vor dem Import geöffnet war. Kommentieren Sie die Zeile gegebenenfalls aus.

Sub ImportMails()

     cnt = 0

     maxcnt = 5000

     GetStorageFolders

     GetFolders

     GetMails

     m_OL.Quit

     DoEvents

     Set m_OL = Nothing

     MsgBox "Fertig"

End Sub

Listing 5: ImportMails ruft alle benötigten Prozeduren nacheinander auf

E-Mails in Access anzeigen

Die Datenblattansicht der Tabelle tblMails ist zum Lesen der importierten Datensätze wenig geeignet. Also wird ihr zu diesem Zweck ein Formular frmMails spendiert, dessen Datenherkunft unmittelbar an sie gebunden ist.

Zur Laufzeit präsentiert es sich wie in Bild 6. Bei allen Steuerelementen auf der rechten Seite handelt es sich um Textboxen, die an die entsprechenden Datenfelder geknüpft sind. Das Kombinationsfeld oben speist sich aus der Tabelle tblMailFolders, aus der es aufsteigend sortiert die Felder ID und Path bezieht. Damit lässt sich ein Outlook-Ordner anhand des absoluten Pfads auswählen. Darunter befindet sich ein Listenfeld, dessen Einträge auf einem gefilterten SQL-Ausdruck basieren, der die Tabelle tblMails zur Grundlage hat. Die Betreffs der Mails stehen hier in den Zeilen. Ein Klick auf einen Eintrag zeigt rechts den Inhalt und weitere Daten an.

Mit dem Formular frmMails können Sie die importierten Outlook-Mails unmittelbar einsehen und lesen

Bild 6: Mit dem Formular frmMails können Sie die importierten Outlook-Mails unmittelbar einsehen und lesen

Die Auswahl eines Eintrags aus dem Kombinationsfeld cbPath führt zum Ereignis Nach Aktualisierung, was die Prozedur in Listing 6 aufruft. In IDFld wird die ID des Ordners zwischengespeichert und dann als Wert in den Ausdruck sSQL eingebaut. Dort filtert es die E-Mails nach deren Ordner-ID IDFolder, sodass nur die im Verzeichnis enthaltenen E-Mails zurückgegeben werden. Der Ausdruck ist Quelle für die Steuerelementeigenschaft RowSource des Listenfelds lstMails.

Private Sub cbPath_AfterUpdate()

     Dim IDFld As Long

     Dim sSQL As String

     

     IDFld = Nz(Me!cbPath.Value)

     If IDFld = 0 Then

         sSQL = "SELECT tblMails.ID, tblMails.Subject, tblMails.IDFolder FROM tblMails ORDER BY tblMails.DateTime DESC"

     Else

         sSQL = "SELECT tblMails.ID, tblMails.Subject, tblMails.IDFolder FROM tblMails" & _

                " WHERE IDFolder=" & IDFld ORDER BY tblMails.DateTime DESC"

     End If

     Me!lstMails.RowSource = sSQL

End Sub

Listing 6: Filtern der Listbox lstMails in Abhängigkeit vom Wert der Combobox cbPath, die die Ordnerpfade der Outlook-Verzeichnisse auflistet

Ist IDFld 0, was der Auswahl Alle in der Combobox entspricht, so findet über die If-Bedingung keine Filterung statt. Diesen speziellen Eintrag gibt es in der Datensatzherkunft der Combobox deshalb, weil sie tatsächlich eine UNION-Abfrage verwendet:

(SELECT 0 As ID, "-Alle-" As Path

   FROM tblMails)

UNION

(SELECT ID, Path FROM tblMailFolders

    WHERE ID In

   (SELECT IDFolder FROM tblMails)

ORDER BY Path

)

Bei Klick auf einen Listenfeldeintrag wiederum wird dessen Ereignis Nach Aktualisierung ausgelöst. Hier kommt diese Prozedur ins Spiel:

Private Sub lstMails_AfterUpdate()

     Me.Filter = _

"ID=" & Me!lstMails.Value

     Me.FilterOn = True

End Sub

Der Formularfilter wird eingeschaltet, wobei die ID der E-Mail den gleichen Wert haben soll, wie der Wert des im Listenfeld lstMails ausgewählten Eintrags. Damit kommt im Formular die gewünschte E-Mail zur Ansicht.

Übrigens öffnet sich das Formular erst gar nicht, wenn noch keine Mails importiert wurden. Dafür ist seine Ereignisprozedur Beim Öffnen verantwortlich:

Sub Form_Open(Cancel As Integer)

If CurrentDb.TableDefs("tblMails"). _

                   RecordCount = 0 Then

     ...Meldung MsgBox "..."

     Cancel = True

End If

End Sub

Die Anzahl an Datensätzen der Tabelle tblMails wird über deren TableDef-Objekt abgefragt, was direkt über die Methode RecordCount gelingt und schneller ist, als die Aggregatfunktion DCount von Access. Ist die Zahl 0, so wird der Übergabeparameter Cancel auf True gesetzt, was das Formular schließt, bevor es sichtbar wird. Eine Msgbox informiert zusätzlich über das Manko.

Interessant wird es, wenn eine E-Mail im HTML-Format ausgewählt wird. Dann soll ja ein Webbrowser-Steuerelement zur Tat schreiten. Dieses Element ctlWeb befindet sich exakt hinter dem Textfeld für den Body im Formular. Das jedoch nur scheinbar. Da es sich beim ab Access 2007 verfügbaren Webbrowser-Steuerelement genauso um ein ActiveX-Steuerelement handelt, wie beim Microsoft Webbrowser-ActiveX, kann es nicht hinter ein Access-Steuerelement gebracht werden.

Das erlauben ActiveX-Steuerelemente grundsätzlich nicht. Sie können es aber auf Unsichtbar schalten, indem Sie seine Visible-Eigenschaft steuern. Im Entwurf steht Sichtbar bei ihm auf Nein. Im Ereignis Beim Anzeigen, welches bei jedem Datensatzwechsel auftritt, stellt die Routine aus Listing 7 die Sichtbarkeit jeweils erneut ein.

Private Sub Form_Current()

     Dim bHTML As Boolean

     

     bHTML = Me!IsHTML.Value

     If InStr(1, Me!Body.Value, "") > 0 Then bHTML = True

     Me!ctlWeb.Visible = bHTML

     If bHTML Then

         Dim oDoc As Object

         Me!ctlWeb.Object.Navigate2 "about:blank"

         Do

             DoEvents

         Loop Until Me!ctlWeb.Object.ReadyState >= 3

         Set oDoc = Me!ctlWeb.Object.Document

         oDoc.Write Me!Body.Value

     End If

End Sub

Listing 7: Ansteuern des Webbrowser-Steuerelement im Ereignis Beim Anzeigen

Das Datenfeld IsHTML gibt Auskunft darüber, ob die E-Mail im HTML-Format verfasst ist. Sein Wert wird in der Hilfsvariablen bHTML zwischengespeichert. Es kommt allerdings vor, dass ein MailItem für BodyFormat ein olFormatPlain ausgibt, was Normaltext bedeutet, aber tatsächlich in HTML verfasst wurde. Dies passiert manchmal beim Import von Mails aus anderen Dateien. Deshalb testet die Routine mit Instr, ob das Tag im Text vorkommt. Auch dann setzt sie bHTML auf True.

Wenn die HTML-Bedingung schließlich erfüllt ist, muss das Steuerelement den Inhalt auch rendern. Leider kann man den Webbrowser dafür nicht direkt an ein Datenfeld binden. Stattdessen erfolgt ein Aufruf seiner Methode Navigate2 mit about:blank, was ihm das Erzeugen einer leeren Seite anweist. Deren Fertigstellung muss nun erst abgewartet werden. Die Do-Loop-Schleife wird erst verlassen, wenn die Eigenschaft ReadyState des Webbrowsers einen Wert größer 2 aufweist. Dann erst ist sein Document-Objekt gültig, und es wird an die Modulvariable oDoc (Typ Object) verwiesen. Ein HTML-Document kennt die Methode write, die es erlaubt, den Inhalt des Dokuments, genauer des Body, neu zu schreiben. Und der kommt nun aus dem Datenfeld Body der Tabelle.

Bild 7 zeigt ein Beispiel. In der Mail verlinkte Bilder werden, im Unterschied zu Outlook selbst, übrigens immer downgeloaded. Das ließe sich gegebenenfalls durch Auswerten des Ereignisses BeforeNavigate2 abfangen.

Für E-Mails im HTML-Format kommt im Formular ein eingebettetes Webbrowser-Steuerelement zum Einsatz

Bild 7: Für E-Mails im HTML-Format kommt im Formular ein eingebettetes Webbrowser-Steuerelement zum Einsatz