Exchange 200x Store  Sinks

Store Event Sinks gibt es erst seit Exchange 2000 und sind auch bei Exchange 2007 weiterhin nutzbar und erforderlich, wenn man "synchron" arbeiten muss. für asynchrone Verarbeitungen können auch Webservices Notification.

Ehe Sie über "Store Eventsinks" nachdenken, sollten Sie wissen, dass hier eine sehr gute Code Qualität gewährleistet sein muss. Schließlich startet der Exchange Store selbst (STORE.EXE) dann ihren Code für bestimmte Ereignisse. Man kann mit solchen Sinks nahezu alles in Exchange verändern und natürlich auch Exchange an die Wand drücken.

Storesink Grundlagen

Normalweise muss man seinen Code erst mal als "COM"-DLL bauen und auf dem Server installieren. Dieser Code muss die folgenden Methoden unterstützen:

  • OnDelete
    Startet asynchron, wenn der Benutzer eine Mail löscht, verhindert aber nicht das Löschen
  • OnSave
    Startet asynchron, wenn ein Objekt erstellt oder geändert gespeichert wird.
  • OnSyncSave
    Wird vor dem finalen Speichern aufgerufen. Programm kann Inhalte noch ändern
  • OnSyncDelete
    Wird vor dem finalen Löschen aufgerufen. Programm könnte Infos extrahieren, sichern oder sogar das Löschen verhindern
  • OnSyncLock, OnSyncUnlock
  • OnTime
  • OnMdbStartup, OnMdbShutDown

Die "OnSync"-Funktionen laufen synchron ab, d.h. die Weiterverarbeitung dieses Objekts wird solange verzögert, bis ihr eigener Code beendet ist. Die beiden ersten Events starten ihr Skript, aber lassen die Speicherung der Änderung zu. Wer also nur "mitschneiden" will, was im Store gelöscht wird, der kann mit "OnDelete" arbeiten. Wer aber Löschungen durch den User verhindern oder Speicherungen modifizieren will, sollte mit OnSyncDelete und OnSyncSave implementieren. Bei NetatWork haben wir schon einige Sinks entwickelt, die z.B. Verhindern, dass ein PDA mit ActiveSync die Messageclass von einem benutzerdefinierten Formular auf das Standardformular zurück ändert. (OnSyncSave). Mti einem "OnSave"-Event könnte man z.B.: dafür sorgen, dass Änderungen in Kontakten als Kopie in eine Datenbank übertragen werden oder ins Archiv kopiert werden. Der Einsatzbereich von StoreSinks ist sehr breit.

Nachdem Sie ihren Code also entwickelt und installiert haben, müssen Sie Exchange noch mitteilen, dass er diesen Code ausführen soll. Auch hierzu gibt es ein VBScript von Microsoft, welchem man einige Parameter und Filter übergeben muss:

  • Aktion
    Zur Auswahl stehen die vier Optionen "onsave","ondelete", "onsyncsave", "onsyncdelete". Man kann mehrere Optionen mit ";" getrennt angeben
  • Ordner oder "Global
    Ein Sink wird auf einen Ordner registriert, für den es gültig ist. Das ist der ideale Weg um Sinks erst mal zu testen. Wenn man den Sink aber auf die Systemmailbox registriert, dann gilt er für alle Postfächer in diesem Store
  • Nachrichtentyp
    Über Filter kann man z.B.: festlegen, dass weitere Kriterien wie die Messageclass gelten. Das ist z.B.: interessant, wenn Sie mit einem globalen Sink nur die Kontakte aller Benutzer in einer Datenbank weiter verarbeiten möchten.

Es ist immer eine gute Idee, nur die Datenquellen anzuzapfen, die auch erforderlich sind. Wenn sie nur wenige Ordner bearbeiten wollen, dann sollten Sie besser diese Ordner einzeln "registrieren", statt mit einer globalen Registrierung alle Mails an ihren Code zu geben und dort dann erst die unerwünschten zu verwerfen. Storesinks können auch schnell die Exchange Performance beeinträchtigen.

Bedenken Sie auch immer, dass Exchange "Multithreaded" ist und ihr Code daher auch parallele Aufrufe korrekt verarbeiten muss und es keine Sperren gibt. Gerade bei "OnSync"-Aufrufen kann ihr Exchange Server dann sehr schnell "stehen".

Globale Store Event Sinks

Neben den Sinks, die Sie auf einzelne Ordner und Ordnerstrukturen anwenden können, gibt es auch die Option, einen globalen Sink auf dem Store selbst zu registrieren. An diesen Code müssen natürlich noch höhere Anforderungen gestellt werden, da er für sehr viele Aktionen aufgerufen werden kann. Der Einsatzbereich eines solchen globalen Sink kann sehr vielfältig sein. Denkbar sind z.B.:

  • Archivierung
    Ein solcher Sink erhält ja alle Schreibvorgänge auf Objekte und kann an der Stelle quasi in Echtzeit veränderte Objekte einfach als Kopie an das Archiv senden. (Archivieren mit Exchange)
  • Changemanagement und Auditing
    Wer hat wann welche Mail gelöscht könnte damit problemlos protokolliert werden. Allerdings gibt es auch im Store selbst schon die Funktion ein Auditing auf Ordner zu aktivieren. Allerdings gibt es kaum eine GUI und noch weniger eine Auswertesoftware dafür.
  • Plausibilitätsprüfungen
    Der Sink könnte das Objekt selbst auf seine Gültigkeit prüfen und bei Bedarf korrigieren oder das Abspeichern verhindern
  • Workflow
    Ebenso könnte solche ein Sink z.B.: bei jeder eingehenden Mail prüfen, ob der Anwendern vielleicht in Urlaub oder abgemeldet ist und die Mail entsprechend ohne Zutun des Anwenders weiterleiten. Auch einfache Workflows wären damit möglich.
  • Datenreplikation
    Natürlich könnte man so auch einen Weg finden, um Daten aus einem Postfach in ein anderes Postfach zu kopieren oder in andere Datenquellen zu übertragen.

Da stellt sich natürlich die Frage, ob so ein globaler Sink nicht die Performance des Servers allzu stark beeinflusst. Leider gibt es keine klare Antwort darauf, da vieles davon abhängt, wie intelligent der Sink geschrieben ist und was er tut. Er muss auf jeden Fall reentrant sein, damit mehrere Prozesse parallel ablaufen können. Zudem sollte er eben nicht allzu lange blockieren, da dies der Anwender sicher merken würde. für eine Zusatzfunktion auf einem einzelnen Ordner gleich einen globalen Sink zu registrieren ist sicherlich überdimensioniert. Allerdings ist es auch nicht gerade der Performance zuträglich, wenn Sie viele einzelne gleichartige Sinks entwickeln und an den betreffenden Ordner binden. Hier ist demnach Testen angesagt.

Wenn Sie hingegen nur protokollieren wollen, wer einen öffentlichen Ordner löscht, dann könnte folgender Artikel interessant sein:

Eine Besonderheit bei Store Event Sinks ist die Einrichtung als globaler Sink. Wird der Sink auf die System Mailbox registriert, dann wird der Sink für "JEDE" Aktion auf dem Informationsspeicher dieser Mailbox zweimal ausgeführt:

  • Vor dem Speichern
    Damit kann der Sink das Objekt als ersten sehen und verändern.
  • Nach dem Speichern
    Damit erhält der Sink das Objekt, nachdem z.B. andere Sinks das Objekt verändert haben.

Das muss man also als Entwickler auch berücksichtigen, damit der eigene Code nicht doppelt aktiv wird.

Obwohl Microsoft Support etwas anderes erzählt, kann man wohl sogar globale Sinks auf öffentliche Ordner einrichten. Verlassen würde ich mich darauf aber nicht, dass es mit jeder Version funktioniert.

Mustersink mit VBScript

Auch wenn man es "eigentlich" nicht tun sollte, so kann man tatsächlich auch mit VBScript einen Store Event Sink programmieren. Im TechNet Artikel "895239 How to register a store event sink using Regevent.vbs on Exchange" ist hierzu sogar eine Schritt für Schritt Anleitung. Man benötigt hierfür jedoch eine "Helper.DLL", die Microsoft bei Exchange 2000 mitliefert aber nicht registriert hat. Der Exchange Store startet dann eigentlich diese HelperDLL, die dann ihrerseits das VBScript aufruft. Entsprechend sind dann auch die Schritt zu tun:

Dies ist ein Beispiel für wenig Last. Eine Anwendung auf einen aktiven Store mit vielen Benutzern und vielen Systemen ist definitiv nicht ratsam.

Für diese Demonstration gibt es einen Infofilm unter dem Namen storesink.wmv (4MB)

Die hier verwendeten Beispiele und Skripte finden Sie hier als Archiv.
storesink-sample.zip

  • VBScript schreiben und abspeichern als C:\msxfaq.samplesink\msxfaqsamplesink.vbs
    z.B. folgenden Mustercode, der einfach alle "Löschbefehle" asynchron in eine Protokolldatei schreibt.
<SCRIPT LANGUAGE="VBScript">

public Sub ExStoreEvents_OnDelete(pEventInfo, bstrURLItem, lFlags)
	Dim fso
	Dim txtfile
	Set fso = CreateObject("Scripting.FileSystemObject")
	Set txtfile = fso.OpenTextFile("c:\msxfaq.samplesink\msxfaq.samplesink.log", 8, True)
	txtfile.WriteLine ("Object geloescht:" & bstrURLItem)
	txtfile.Close
End Sub

</SCRIPT>

Sie können das Skript nicht per CSCRIPT einfach aufrufen. CScript stört isch an den >Script> Tags.

  • Registrieren Sie die Hilfs-DLLs
    Normalerweise unterstützt Exchange keine VBScripte als StoreSinks. Aber seit Exchange 2000 gibt es doch eine DLL, die als Vermittler zwischen den beiden Welten agiert. Diese sind aber zu registrieren.
regsvr32 C:\Programme\exchsrvr\bin\exodbesh.dll
regsvr32 C:\Programme\exchsrvr\bin\exodbprx.dll
  • COM-Application erstellen
    Als nächstes Registrieren Sie die Datei "Exchsrvr\Bin\exodbesh.dll" als COM-Komponenten in einer leeren COM-Applikation. Starten Sie dazu die MMC für Komponenten Dienste (Administrative Tools), legen Sie eine neue Anwendung an und darunter die DLL als neue Komponenten (Genaueres HowTo ist in "895239 How to register a store event sink using Regevent.vbs on Exchange" beschrieben. Sie können Sich das Leben noch einfacher machen und einfach das Skript "eshmts.vbs" aus dem Exchange SDK starten
    COM-Anwendung
    Wichtig ist, dass der Account, mit welchem der Sink später läuft, die erforderlichen Rechte in dem entsprechenden Ordner hat, auf den er registriert wird. Siehe auch "Exoledb Script Host Event Sink Debugging and Troubleshooting  http://www.outlookexchange.com/articles/glenscales/shesdat.asp". Achten Sie auch darauf, dass es GENAU EINEN solchen Eintrag gibt.
  • Sink registrieren
    Nun brauchen wir das VBScript "regevent.vbs" aus dem Exchange SDK (C:\Programme\Exchange SDK\SDK\Support\OLEDB\Scripts), welches den Sink im Exchange Store hinterlegt. Die Parameter sehen erst einmal etwas kryptisch aus, aber sind schnell zu verstehen.
    Parameter1: add (Hinzufügen) , enum (Aufzählen) ,. delete (löschen)
    Parameter2: Die URL zum Ordner in der Datenbank
    Option -file: Dateiname des Skripts.
    Weitere Optionen sind auf http://msdn2.microsoft.com/en-us/library/aa123692.aspx beschrieben. Ruft man regevents.vbs einfach so auf, dann wird eine Hilfe angezeigt. Der Aufruf muss aber auf dem Exchange Server mit administrativen Berechtigungen erfolgen.
cscript regevent.vbs add "ondelete" ExOleDB.ScriptEventSink.1
  "file://./backofficestorage/msxfaq.de/MBX/Administrator/Posteingang/msxfaqsamplesink.evt"
  -m deep -file "c:\msxfaq.samplesink\msxfaqsamplesink.vbs"

Achtung: Sinks können nicht auf "Gesendete Objekte" oder den "Postausgang" registriert werden
Siehe auch Q297274 PRB: Store Events Do Not Fire on the Outbox or Sent Item Folders

Anders als bei Storesinks, die als COM-Objekt eingebunden werden, wird das Skript in den fraglichen Ordner kopiert. Auch wenn es verborgen ist, kann damit theoretisch auch ein Anwender diesen Code sehen und sogar ändert, der dann vom Server ausgeführt wird. Spätestens hier sehen Sie dann, dass diese Beispielanwendung nicht produktiv gehen darf.

Der Sink wirkt dann auf den Posteingang. Genau diesen Sink können Sie mit folgender Zeile wieder entfernen.

cscript regevent.vbs delete 
  "file://./backofficestorage/msxfaq.de/MBX/Administrator/Posteingang/msxfaqsamplesink.evt"

Sink mit dem Exchange Explorer registrieren

Wem die letzte Kommandozeile zu kompliziert ist, kann auch mit dem Exchange Explorer (Ebenfalls im Exchange SDK) auch per grafischer Oberfläche einen Sink eintragen und entfernen.

Exchange Explorer

Danach startet ein Assistent, der Sie nacheinander alle Optionen abfragt:

  • Name des Sinks
    Name des Sinks
  • Aktivierungsart
    Aktivierungsart
  • Events
    Events
  • Kriterien
    Kriterien
  • Sinkart: COM oder Skript
    Sinkart
  • Pfade zum Skript oder die ProdID des COM-Objects
    Skriptpfad oder COM ProgID
  • Sicherheitsabfrage
    Sicherheitsabfrage
  • Abschluss
    Wenn kein Fehler kommt, dann wurde der Sink zumindest registriert. Es ist natürlich nicht sicher, dass er dann auch funktioniert. Folgende Fehlermeldung erscheint, wenn Sie z.B.: die Hilfsklassen noch nicht als COM-Anwendung konfiguriert haben

    Meldung bei fehlender COM-Anwendung auf dem Server

    Man kann den Sink aber auch in dem Ordner als verborgene Nachricht finden
    Eventsink im Exchange Explorer anzeigen und anpassen
    Sogar verändern können Sie den Sink an dieser Stelle

Hintergründe und Debugging

Im Hintergrund wird das Skript als verborgende Nachricht in den entsprechenden Ordner kopiert. Der Anwender kann das Skript nicht sehen. Sollte das Skript aber sichtbar sein (weil ein Virenscanner z.B. beim Anlegen die Eigenschaften verhindert hat, dann wird es nicht gestartet. Diesen "Trick" nutzen viele andere Programme ebenfalls um Konfigurationsdaten zu verbergen. So speichert Outlook 2007 seine RSS-Feed Informationen. Sehen kann man diese Informationen z.B. mit MFCMAPI. oder dem Exchange Explorer oder Mistaya. Wenn Sie weitere Fehler haben, dann schauen Sie sich mal die folgende Seite an:

  • Exoledb Script Host Event Sink Debugging and Troubleshooting
    http://www.outlookexchange.com/articles/glenscales/shesdat.asp
  • 316622 Event sinks do not function correctly if the SystemMailbox{GUID} mailbox is absent from a mailbox store in Exchange Server 2003 or in Exchange 2000 Server
  • Eventlog EXOLEDB Quelle
    Auch eine Suche nach Meldungen im Eventlog von ExOLEDB bringt oft wertvolle Hinweise wie:
Event Type:       Error
Event Source:   EXOLEDB
Event Category:               Events
Event ID:             116
Computer:         SRV01
Description:
Microsoft Exchange OLEDB was unable to initialize event system correctly.
Support für store event may have been disabled. HRESULT = 0x8000ffff.
Event Type:       Error
Event Source:   EXOLEDB
Event Category:               Events
Event ID:             116
Computer:         SRV01
Description:
Microsoft Exchange OLEDB was unable to initialize event system correctly.
Support für store event may have been disabled. HRESULT = 0x80004005.

Auch im Windows Performance Counter System kann man die Aktivität der Sinks sehen

StoreSink Performance Counter

Weitere Links

Hier nur eine kleine Auswahl von Links. Die MSDN-Seite ist sicher die beste Quelle

Produkte, die Storesinks nutzen