Alle Microsoft-Office-Programme wie Word, Excel usw. bauen intern auf der COM-Architektur von Windows auf. Sie bilden ein komplettes Framework, das sich durch eben diese standardisierte Schnittstelle von jeder COM-fähigen Windows-Programmiersprache automatisieren lässt. Die Technik, externe Programmobjekte über eine standardisierte Schnittstelle in die eigene Anwendung einzubauen nennt man OLE Object Linking and Embedding bzw. OLE2 als deren Erweiterung. Bis hierhin ist die ganze Sache völlig unabhängig von SAP und ABAP. Bei den Enjoy-Controls haben wir bereits OLE2-Objekte automatisiert. Diese Objekte existierten im Hauptspeicher des lokalen Anwender-Rechners, und ihre Oberfläche wurde in die Oberfläche des SAP GUI hineinprojiziert. Bei der Ansteuerung der O- LE2-Objekte, die zu MS Office gehören, gibt es diese direkte Verknüpfung der Oberfläche nicht mehr. Das ist aber auch schon alles, was sie von den bisherigen unterscheidet. Insbesondere die Technik der Automation Queue bleibt erhalten. Bei den Enjoy-Controls hatten wir es in der Regel auch nur mit einem Objekt zu tun, das wir angesteuert haben. Da hinter dem Office-Framework weitaus kompliziertere Strukturen stecken, bilden diese ein hierarchisches Netz von Objekten. Abbildung 6.1 demonstriert den Zusammenhang zwischen ABAP-Programm, GUI und Office-Framework. ABAP-Programm OLE2-Schicht Objektoperationen Automation Queue SAP GUI Applikationsserver Rechner des Anwenders Abb. 6.1: schematische Darstellung der Kommunikationskanäle 225
6.1 Ansteuerung von OLE2-Objekten Erste Voraussetzung ist die Einbindung des OLE2-Includes. Es enthält alle nötigen Datentypen; insbesondere einen Typ namens ole2_object, der das Verbindungsstück zwischen ABAP-Code und der OLE2-Schicht herstellt. INCLUDE ole2incl. DATA: <ObjektReferenz> TYPE ole2_object. Ein OLE2-Objekt wird wie üblich mit Hilfe des Befehls CreateObject instanziiert. Der Konstruktor empfängt den Namen der O- LE2-Klasse auf dem Zielsystem. Er ist eindeutig und in der lokalen Registrierdatenbank des Anwender-Rechners hinterlegt. CREATE OBJECT <ObjektReferenz> '<KlassenNameDesOLE2Servers>'. Das folgende Beispiel erzeugt ein Word-Objekt. Es wird für den Anwender noch nicht zu bemerken sein, weil es nur im Hintergrund erzeugt wird. Lediglich ein Blick in den Task-Manager zeigt einen Prozess namens Windword.exe. INCLUDE ole2incl. DATA: wordobj TYPE ole2_object. CREATE OBJECT wordobj 'Word.Application'. OLE2-Objekte verfügen über Eigenschaften. Diese können entweder gesetzt oder ausgelesen werden. Das Setzen und Lesen von Eigenschaften wird über die Befehle SET PROPERTY bzw. GET PROEPRTY erledigt. Anzugeben ist jeweils das referenzierte Objekt sowie der Name der Eigenschaft, auf den sich die Wertübergabe beziehen soll. SET PROPERTY OF <ObjektReferenz> '<NameDerEigenschaft>' = <ZuSetzenderWert> 226 bzw.
6.1 Ansteuerung von OLE2-Objekten GET PROPERTY OF <ObjektReferenz> '<NameDerEigenschaft>' = <ZuLesenderWert> Im Beispiel oben haben wir ein Anwendungsobjekt von Word erstellt. Dieses Objekt hat vorerst keinerlei Oberfläche, denn es ist unmittelbar nach der Instanziierung lediglich im Hauptspeicher vorhanden, ohne dass es der Anwender merkt. Neben etlichen anderen Eigenschaften des Anwendungsobjekts steht uns Visible zur Verfügung. Standardmäßig steht es auf false bzw. 0, die Anwendung ist unsichtbar. Mittels SET PROPERTY OF wordobj 'Visible' = 1 zeigt sich das Word-Objekt in seiner vollen Pracht und öffnet die Anwendung. Wenden wir uns den Methoden zu. Sie bilden den komplexesten Aufruf, da Methoden beliebig viele Übergabeparameter haben können. Außerdem ist eventuell noch ein Rückgabewert ermittelbar. Die Syntax lautet wie folgt: CALL METHOD OF <ObjektReferenz> '<NameDerMethode>' = <Rückgabewert> EXPORTING <NameDesÜbergabeParameters1> = '<Wert1>' <NameDesÜbergabeParameters2> = '<Wert2>' Auf die einzelnen Übergabe-Parameter kann entweder mit dem Namen oder der Ordinalzahl zugegriffen worden. Im letzteren Fall ist der Ordinalzahl jeweils ein # voranzustellen. Das Word-Objekt unseres Beispiels bietet unter anderem die Methode CheckSpelling. Sie veranlasst das Word-Objekt, das als Parameter übergebene Wort einer Rechtschreibprüfung zu unterziehen. 227
DATA ret TYPE I. CALL METHOD OF wordobj 'CheckSpelling' = ret EXPORTING #1 = 'Fähler'. Im vorliegenden Fall sollte ret nach dem Aufruf mit 0 gefüllt sein, denn die Rechtschreibprüfung findet hoffentlich den Fehler. Zu guter Letzt zerstören wir nach getaner Arbeit das instanziierte Objekt mittels FREE OBJECT <ObjektReferenz> Es wird aus dem Speicher entfernt, und die Objektreferenz ist somit ungültig. Nachdem die Theorie über OLE-Objekte nun soweit klar sein sollte, werden wir in den folgenden Kapiteln in das Handling von Office-OLE-Objekte eintauchen. Natürlich ist diese Technik nicht auf Office beschränkt. Viele (auch Microsoft-unabhängige) Anwendungen gehorchen derselben Architektur und können mit dieser Technik angesteuert werden. 6.2 Excel In diesem Teilkapitel werden wir uns die Objekthierarchie von MS Excel ansehen. Abbildung 6.2 zeigt, wie die Objekte untereinander zusammenhängen. Den Begriff der Collection haben wir bisher in noch keinem Zusammenhang diskutiert. Eine Collection hat lediglich die Aufgabe, eine beliebige Anzahl an Objektreferenzen zu halten. So wie die Workbooks-Collection, die direkt unter dem Anwendungsobjekt hängt. Sie hält Objektreferenzen auf die Klasse Workbook. Diese wiederum repräsentiert eine einzelne geöffnete Excel-Datei. Analog dazu kann jede Excel-Datei beliebige viele Tabellenblätter beinhalten. Daher hält die Collection Worksheets Referenzen auf die Objekte der Klasse Worksheet, die ein einzelnes Tabellenblatt repräsentieren. Innerhalb eines Tabellenblattes gibt es dann nur noch die Range- Klasse, die bis auf eine einzelne Zelle hinuntergeht. 228
6.2 Excel Excel.Application Das oberste Anwendungsobjekt Excel.Workbooks Collection aller geöffneten Dateien Excel.Workbook Geöffnete Excel-Datei Excel.Worksheets Collection aller Tabellenblätter Excel.Worksheet Tabellenblatt Excel.Range Eine oder mehrere Zellen eines Tabellenblattes. Abb. 6.2: Excel-Objekthierarchie Die hier erwähnten Excel-Objekte sind nur eine Auswahl derer, die es überhaupt gibt. Eine Liste aller Klassen und deren Eigenschaften und Methoden lässt sich in Excel selbst ansehen: Über Extras -> Macros -> Visual Basic-Editor in die VBA- Entwicklungsumgebung abspringen und dort auf Ansicht -> Objektkatalog. Im Objektkatalog wählen wir in der Bibliotheks- Combo oben links Excel. Abbildung 6.3 zeigt diese Übersicht. Im linken Bereich der Klassen ist in der Abbildung die Klasse Workbook gewählt. Im rechten Bereich werden dann die zugehörigen Methoden und Eigenschaften aufgelistet. 229
Abb. 6.3: Klassenbibliothek in der VBA-Entwicklungsumgebung 6.2.1 Beispiel ZBIB_OI01 Im Folgenden wollen wir ein Beispielprogramm schreiben, dass eine Datenbanktabelle nach Excel exportiert. Die Tabelle soll grau-gefärbte Überschriften und eine Summenzeile bekommen. Der Aufruf von OLE2-Methoden ist relativ schwierig zu debuggen, aus diesem Grund ist es nötig, nach jedem Aufruf den sysubrc abzufragen, ob ein Fehler aufgetreten ist. Um nicht jedes Mal eine mehrzeilige Fehlerbehandlung tippen zu müssen, standardisieren wir die Fehlerbehandlung in einer Unter-Routine. Der Übergabeparameter text soll dann jeweils in Klarschrift die aufrufende Funktion enthalten. FORM checkerror USING text. IF sy-subrc <> 0. WRITE: / 'Fehler bei: ',text, sy-subrc. STOP. ENDIF. ENDFORM. Zellenwerte setzen Es lassen sich noch einige wiederkehrende Aufgaben ebenso kapseln. Die folgende Unter-Routine füllt eine einzelne Zelle eines Tabellenblattes mit einem Wert (value). Dazu wird die gewünschte Zeile (z), die gewünschte Spalte (s) und ein 230
6.2 Excel Worksheet-Objekt (worksheet) übergeben. Die Methode Cells des Worksheet-Objekts gibt unter Angabe der Zeile und Spalte ein Range-Objekt zurück, das die gewünschte Zelle repräsentiert. Das Attribut Value des Range-Objekts kann dann einfach auf den Übergabewert gesetzt werden. FORM zellefuellen USING z type i s type i value worksheet type ole2_object. DATA excelrange TYPE ole2_object. CALL METHOD OF worksheet 'Cells' = excelrange EXPORTING #1 = z #2 = s. PERFORM checkerror USING 'Get Range'. SET PROPERTY OF excelrange 'Value' = value. PERFORM checkerror USING 'Range.Value =..'. ENDFORM. Zellen einfärben Analog dazu die Unter-Routine zellefaerben. Sie versieht die gewünschte Zelle mit einer Hintergrundfarbe. Hierzu besorgen wir uns zuerst ein Objekt der Klasse Interior. Es enthält Attribute wie Farb- und Formatangaben zu einem Range-Objekt. FORM zellefaerben USING z TYPE i s TYPE i farbe TYPE i worksheet TYPE ole2_object. DATA: excelrange TYPE ole2_object, excelinterior TYPE ole2_object. 231
CALL METHOD OF worksheet 'Cells' = excelrange EXPORTING #1 = z #2 = s. PERFORM checkerror USING 'Get Range'. CALL METHOD OF excelrange 'Interior' = excelinterior. PERFORM checkerror USING 'Get Range.Interior'. SET PROPERTY OF excelinterior 'ColorIndex' = farbe. ENDFORM. Hauptprogramm Nachdem die Vorarbeit erledigt ist, sehen wir uns das Hauptprogramm an. Wir müssen den kompletten, in Abbildung 6.2 gezeigten Objektbaum instanziieren. Angefangen am Application- Objekt, aus dem wir uns ein Objekt der Workbooks-Collection besorgen. Dieses wiederum fügt mit Add ein neues Workbook ein, aus dem wir uns die Worksheets-Collection holen. Dieser fügen wir ebenfalls per Add ein neues Tabellenblatt der Klasse Worksheet hinzu. REPORT zbib_excel01. DATA pfile(100) TYPE c. pfile = 'c:\test.xls'. INCLUDE ole2incl. DATA: excelobj TYPE ole2_object, excelwbs TYPE ole2_object, excelwb TYPE ole2_object, excelwss TYPE ole2_object, excelws TYPE ole2_object. TABLES: zbibverlage. DATA it_verlage LIKE zbibverlage OCCURS 0 WITH HEADER LINE. DATA: zeile TYPE i. 232
6.2 Excel SELECT * FROM zbibverlage INTO TABLE it_verlage. Application- Objekt erzeugen CREATE OBJECT excelobj 'EXCEL.APPLICATION'. PERFORM checkerror USING 'Create EXCEL.APPLICATION'. SET PROPERTY OF excelobj 'Visible' = 1. Workbooks- Collection CALL METHOD OF excelobj 'Workbooks' = excelwbs. PERFORM checkerror USING 'Get Workbooks'. CALL METHOD OF excelwbs 'Add' = excelwb. PERFORM checkerror USING 'Workbooks.Add'. Worksheets- Collection CALL METHOD OF excelwb 'Worksheets' = excelwss. PERFORM checkerror USING 'Get Worksheets'. CALL METHOD OF excelwss 'Add' = excelws. PERFORM checkerror USING 'Add Worksheet'. SET PROPERTY OF excelws 'Name' = 'Übersichtblatt'. Nachdem wir unser Tabellenblatt nun in Händen halten, können wir darauf die vorbereiteten Routinen zum Füllen und Färben der einzelnen Zellen loslassen. Zunächst die Überschriften: USING 1 1 'VerlagsNr' excelws. USING 1 2 'Verlagsbezeichnung' excelws. USING 1 3 'Anzahl ausgeliehene Bücher' excelws. Nun einmal durch die interne Tabelle loopen und jeden Einzelwert jeder Zeile in das Tabellenblatt schreiben. Danach werden die drei Überschriftszellen mit zellefaerben eingefärbt. LOOP AT it_verlage. zeile = sy-tabix + 1. USING zeile 1 it_verlage-verlag excelws. 233
USING zeile 2 it_verlage-verlagbez excelws. USING zeile 3 it_verlage-anzahl excelws. ENDLOOP. PERFORM zellefaerben USING 1 1 15 excelws. PERFORM zellefaerben USING 1 2 15 excelws. PERFORM zellefaerben USING 1 3 15 excelws. Summenzeile Bleibt uns noch die Summenzeile. Mit Hilfe von CONCATENATE wird die Formel zusammengebaut, genauso, wie man sie in Excel auch schreiben würde. Danach ist die Tabelle wunschgemäß übertragen. Mit der SaveAs-Methode des Workbook-Objekts wird das Excel-File lokal auf Platte gespeichert. DATA: formel(100) TYPE c, n_zeile(5) TYPE n. MOVE zeile TO n_zeile. Formel zusammenbauen...... und übergeben CONCATENATE '=SUM(C2:C' n_zeile ')' INTO formel. zeile = zeile + 1. USING zeile 2 'Summe' excelws. USING zeile 3 formel excelws. Speichern zun Schluss CALL METHOD OF excelwb 'SaveAs' EXPORTING #1 = pfile. PERFORM checkerror USING 'saveas'. FREE OBJECT excelobj. WRITE / 'Programm erfolgreich beendet'. 234