Trojanische Kontextmenüs am Beispiel Excel

Die guten alten Trojaner lassen uns nicht in Ruhe – auch wenn das trojanische Pferd eigentlich gar kein trojanisches, sondern ein griechisches Pferd war: Der Begriff des Trojaners verfolgt uns sogar bis ins Computerzeitalter. Der vorliegende Beitrag beschäftigt sich auch mit diesem Thema, allerdings geht es hier um ein trojanisches Kontextmenü, mit dem Sie von Access aus Funktionen in andere Office-Anwendungen einschleusen. Dreht der Autor jetzt völlig durch Sie werden sehen: Vielleicht schicken ja auch Sie schon bald einen Trojaner in Richtung Word, Excel oder Outlook …

Ein praxisnahes Beispiel für ein trojanisches Kontextmenü liefert der Beitrag Ferngesteuerter Seriendruck mit Access und Word (www.access-im-unternehmen.de/714). Dort geht es um eine Access-Anwendung, die Seriendruckdokumente auf Basis beliebiger Datenherkünfte erzeugen soll. Dabei öffnet der Benutzer von Access aus eine Word-Instanz mit einem vorhandenen oder auch neuen Dokument, um dieses mit den für diese Anwendung notwendigen Platzhaltern auszustatten. Der Clou dabei: Die Platzhalter, die den Feldern der zuvor in Access ausgewählten Tabelle entsprechen, werden so im Kontextmenü von Word angeboten, dass man die Textmarke nach dem Positionieren der Einfügemarke per Kontextmenü in das Word-Dokument einfügen kann. Außerdem schließt der Benutzer die Bearbeitung des Word-Dokuments ebenfalls mit einer Kontextmenüschaltfläche ab.

Natürlich ist diese Vorgehensweise nicht ganz trivial (sonst würde es ja jeder machen): Die VBA-Funktion, die für das Einfügen der Textmarke im Word-Dokument sorgt, steckt nämlich in einem Modul der Access-Anwendung. Und auch wenn es noch relativ einfach ist, ein Kontextmenü der Word-Anwendung mit eigenen Befehlen zu bestücken oder diese anderweitig zu manipulieren, so reicht es längst nicht aus, einfach die VBA-Funktion des Access-Moduls für die OnAction-Eigenschaft des Kontextmenüeintrags anzugeben: Word wiegelt diesen Versuch mit einer humorlosen Meldung ab, dass die Funktion nicht gefunen werden könne.

Beispiel: Excel

Ein Beispiel für den Einsatz eines trojanischen Kontextmenüs in Word finden Sie in der Beispieldatenbank zum oben genannten Beitrag, daher beschäftigen wir uns hier mit einer praktischen Excel-Anwendung. Dabei möchten wir von Access aus eine Excel-Instanz mit einem zuvor ausgewählten Dokument öffnen, einen Bereich dieses Dokuments referenzieren und dann irgendetwas mit diesem Bereich veranstalten – was, bleibt Ihrer Fantasie überlassen.

Excel-Instanz holen

Bevor wir von Access aus irgendwelche Anpassungen am Menü von Excel durchführen können, müssen wir zunächst eine Excel-Instanz erstellen und einen Verweis darauf in einer Objekt-Variablen speichern. Für mehr Komfort bei der Entwicklung setzen wir zunächst einen Verweis auf die Bibliothek Microsoft Excel x.0 Object Library (VBA-Editor, Menü Extras|Verweise). Falls Sie eine andere Anwendung, wie etwa Word oder Outlook, infiltrieren möchten, binden Sie natürlich den entsprechenden Verweis ein. Und da wir exzessiv mit dem CommandBars-Objektmodell arbeiten werden, kann natürlich auch ein Verweis auf die Bibliothek Microsoft Office x.0 Object Library nicht schaden.

Den Verweis auf die Excel-Instanz und auf das darin erzeugte Excel-Dokument speichern Sie in entsprechenden Objektvariablen, die Sie in einem neuen Standardmodul namens mdlExcel anlegen:

Public objExcel As Excel.Application
Public objWorkbook As Excel.Workbook

Wenn Sie später auf Late Binding umstellen und die Verweise aus dem Projekt entfernen möchten, ändern Sie diese beiden Zeilen wie folgt:

Dim objExcel As Object
Dim objWorkbook As Object

Nachdem objExcel und objWorkbook global verfügbar sind, können wir uns für das Öffnen und Schließen dieser Elemente eigene Prozeduren gönnen. Die erste erzeugt zunächst eine Excel-Instanz und erstellt ein neues Workbook:

Public Sub ExcelStarten()
    Set objExcel = CreateObject("Excel.Application")
    Set objWorkbook = objExcel.Workbooks.Add()
End Sub

Die nächste Routine schließt das Dokument, ohne es zu speichern, und beendet auch die Excel-Instanz:

Public Sub ExcelBeenden()
    objWorkbook.Close False
    Set objWorkbook = Nothing
    objExcel.Quit
    Set objExcel = Nothing
End Sub

Wenn Sie beide hintereinander aufrufen, geschieht gar nichts Sichtbares – was auch logisch ist: Wir haben die Excel-Instanz nicht sichtbar gemacht. Das geschieht mit dieser Anweisung, die Sie nach Ausführen von ExcelStarten im Direktfenster ausprobieren können:

objExcel.Visible = True

Vorerst wollen wir Excel aber gar nicht sichtbar machen. Erstmal statten wir das entsprechende Kontextmenü von Excel mit einem neuen Eintrag aus, der die aktuelle Auswahl zur weiteren Bearbeitung an Access übergibt. In der Prozedur, die diese Aufgabe erledigt, brauchen wir zwei Objektvariablen. Die erste referenziert die Kontextmenüleiste, der wir einen Eintrag hinzufügen möchten, die zweite den neuen Eintrag selbst:

Dim cbr As Commandbar
Dim cbc As CommandBarControl

Bevor wir das neue Steuerelement hinzufügen können, müssen wir herausfinden, welches Kontextmenü überhaupt erscheint, wenn wir mit der rechten Maustaste auf einen Bereich im Excel-Worksheet klicken. Dies bekommen wir leicht heraus. Dazu öffnen wir Excel und ein Workbook mit der Prozedur ExcelStarten und rufen die folgende Routine auf:

Public Sub KontextmenueIdentifizieren()
    Dim cbr As Commandbar
    Dim cbc As CommandBarControl
    For Each cbr In objExcel.CommandBars
        If cbr.Type = msoBarTypePopup Then
            Set cbc = cbr.Controls.Add(1, , , , True)
            cbc.Caption = "Name des Kontextmenüs: " & cbr.Name
        End If
    Next cbr
End Sub

Damit fügen Sie jedem Kontextmenü der gesamten Anwendung einen Eintrag hinzu, der sich am Beispiel des Kontextmenüs einer Zelle wie in Bild 1 auswirkt und einen Eintrag mit der Angabe des Namens der Kontextmenüleiste hinzufügt.

pic001.png

Bild 1: Ermitteln des Namens eines Kontextmenüs

Das Kontextmenü Cell wird auch aufgerufen, wenn Sie einen Bereich aus mehreren Zellen markieren, wir brauchen uns also offensichtlich nur um dieses eine Kontextmenü zu kümmern. Gehen wir also in die Vollen und legen unseren Kontextmenüeintrag an:

Public Sub TrojanermenueAnlegen()
    Dim cbr As Commandbar
    Dim cbc As CommandBarButton
    Set cbr = objExcel.CommandBars("Cell")
    Set cbc = cbr.Controls.Add(1, , , , True)
    With cbc
        .Caption = "Auswahl nach Access übernehmen"
        .BeginGroup = True
        .OnAction = "=AuswahlUebernehmen()"
    End With
End Sub

Und damit beim Anklicken des Eintrags auch etwas geschieht, fügen wir noch schnell eine entsprechende Funktion ins Modul ein, die einfach nur ein Meldungsfenster anzeigen soll:

Public Function AuswahlUebernehmen()
    MsgBox "Auswahl übernehmen"
End Function

Nach dem Aufrufen der Prozedur TrojanerAnlegen und einem Klick auf den neuen Eintrag geschieht allerdings Unerwartetes: Es gibt eine Fehlermeldung, weil die Funktion nicht vorhanden sei. Das konnte ja auch nicht klappen: Woher soll die in Excel angelegte Menüschaltfläche auch wissen, dass sich ihre OnAction-Funktion in einer externen Access-Datenbank befindet

CommandBar per Klasse

Aber wer sich schon einmal mit COM-Add-Ins beschäftigt hat, der weiß auch, dass es Möglichkeiten gibt, anderen Anwendungen Menüeinträge hinzuzufügen, deren Aktion von der hinzufügenden Anwendung ausgeführt wird.

Der Schlüssel ist vermutlich, dass wir das Steuerelement als Objekt mit dem Vermerk WithEvents deklarieren müssen und eine eigene Ereignisprozedur für den Klick auf das Steuerelement anlegen. Die WithEvents-Klausel kann allerdings nur innerhalb von Klassenmodulen verwendet werden, sodass wir uns für ein erstes Experiment einfach ein kleines Formular zum Aufruf von Excel, zum Einbau des Trojanermenüeintrags und zum Unterbringen des Ereignisses bauen. Dieses sieht im Entwurf wie in Bild 2 aus und enthält lediglich eine Schaltfläche namens cmdExceltrojaner. Statt des im bisherigen Beispielcode verwendeten CommandBarControl-Objekts verwenden wir ein Objekt des Typs CommandBarButton – dieses kann gegebenüber dem CommandBarControl-Objekt das Ereignis Click auslösen.

pic002.png

Bild 2: Diese Schaltfläche schickt den Trojaner nach Excel.

Die Deklaration der Excel-Objekte und die beiden Prozeduren ExcelStarten und ExcelBeenden können im Standardmodul mdlExcel verbleiben, den Rest gestalten wir im Klassenmodul des Formulars frmExceltrojaner neu. Als Erstes bringen wir dort die CommandBar-Variable sowie die mit dem Schlüsselwort WithEvents versehene Variable für die Menüschaltfläche unter:

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