von Klaus Gerhardt
Der vorliegende Artikel beschäftigt sich mit der OLE-Automation mit Visual FoxPro (VFP) als Client und Microsoft Excel Version 5.0 bzw. Microsoft Word Version 6.0 jeweils als OLE-Server.
Seit OLE 2.0 beherrscht dieses die OLE-Automation. Mit Hilfe der OLE-Automation ist es möglich, aus VFP heraus ein OLE-Automatisierungs-Objekt von beispielsweise Word oder Excel zu erstellen. Diese Objekte werden dann so programmiert, d.h. es werden dessen Eigenschaften modifiziert bzw. dessen Methoden aufgerufen, wie von jedem anderen Objekt in VFP auch. Allerdings mit dem Unterschied, daß die Programmiersprache des OLE-Automatisierungs-Objektes angewandt werden muß! Daraus folgt, daß natürlich eine Kenntnis des "Objektes" Word oder Excel vorhanden sein muß. Es gibt keine einheitliche Programmiersprache für dieses Vorhaben. Für Word kommt Wordbasic zum Einsatz, für Excel Visual Basic for Applications (VBA).
OLE-Automation funktioniert ohne Benutzereingaben in die Serveranwendung. Es ist eher dafür gedacht, mit Hilfe des OLE-Servers eine Aufgabe zu Bewältigen, für welche dieser besser gerüstet ist. Der Server kann also auch unsichtbar im Hintergrund arbeiten. Bei Excel ist dies einfach zu bewerkstelligen. Wenn die Eigenschaft .visible auf .F. gesetzt ist, arbeitet Excel unsichtbar im Hintergrund und befindet sich dann auch nicht auf der Task-Leiste. Bei Word kann man die Anwendung zum Icon verkleinern. Je unsichtbarer desto besser. Denn wenn der Anwender die sichtbare Serveranwendung anklickt, hat diese den Focus und VFP ist erst mal mattgestellt.
Zum Austesten können die Befehle und Funktion in das Befehlsfenster von Visual FoxPro eingeben werden.
Die Anwendung, welche das Dokument "besitzt", ist der Server. Die Anwendung, aus der heraus die Steuerung erfolgt, ist der Client. Die Änderungen am Dokument nimmt immer der Server vor, auch wenn die Befehle dazu vom Client kommen. Denn nur Excel kann die Änderungen am Excel-Dokument vornehmen, nur Word am Word-Dokument etc.
Folgende Befehle werden in Visual FoxPro verwendet:
In VFP ist nicht viel mehr zu tun als das Objekt (s. Beispiele) zu erstellen und die Gebietskennung einzustellen. Danach ist schon die jeweilige Programmier- bzw. Makrosprache des Servers zu verwenden. Diese Makrosprachen sind jedoch meist nicht gut dokumentiert. Wenn man jedoch, wie bei Excel und Word gut möglich, die jeweiligen Makrorekorder der Anwendung verwendet, um sich Programmteile erst einmal innerhalb der betreffenden Anwendung zu erstelllen, kommt man meist weiter. Dazu gibt es jedoch einiges zu beachten. Hier folgend einige Tips (ohne Anspruch auf Vollständigkeit).
Nach dem Erstellen des Ole-Automatisierungs-Objektes hat dies meist den Focus. Mit Hilfe von Win32Api-Funktionen kann man VFP den Focus zurückgeben (s. Beispiel). Die Funktion SetFocus funktioniert nur unter Windows 3.11. Für Windows 95 und Windows NT muß in den Programmen die Funktion SetForegroundWindow aktiviert werden.
Die von den Makrorekordern erstellten Codeteile in VBA bzw. Wordbasic, die auf Methodenaufrufen basieren, arbeiten mit benannten Argumenten (named Arguments). VFP kann diese jedoch nicht per OLE an den Server weiterreichen. In VFP müssen die Argumente als Paramameter wie bei einem Funktionsaufruf übergeben werden. Sofern nicht mehr benötigt wird, reicht es aus, das erste benannte Argument als Parameter zu übergeben.
Excel:
ActiveWorkbook.saveas
Filename:="OLE_XLS.XLS",
FileFormat:=xlNormal, _ Password:="",
WriteResPassword:="",
ReadOnlyRecommended:= _ False,
CreateBackup:=False
VFP:
xlNormal = -4143
oXLS.activeworkbook.saveas ;
("OLE_XLS.XLS",xlNormal,"","",.F.,.F.)
oder:
oXLS.saveas("OLE_XLS.XLS")
Die Reihenfolge der benannten Argumente für die Funktionsaufrufe (Methodenaufrufe) ist aus der Word-Hilfedatei nicht ersichtlich. Auch die mit Hilfe des Makrorekorders erstellten Makros helfen meist nicht weiter. Von Microsoft gibt es die Datei "Position.Hlp" in Englisch, bzw. "Position.Txt" in Deutsch, worin diese Reihenfolge aufgeführt ist.
Beispiel mit BearbeitenErsetzten, englischer Hilfetext aus "Position.Hlp":
Word:
EditReplace [.Find = text] [.Replace = text]
[.Direction = number] [.MatchCase = number]
[.WholeWord = number]
[.PatternMatch = number]
[.SoundsLike = number] [.FindNext]
[.ReplaceOne] [.ReplaceAll]
[.Format = number] [.Wrap = number]
VFP:
oWord.BearbeitenErsetzen ;
("@Firma1","Fa. Xyz",0,0,0,0,0,.F.,.T.,.F.,0,0)'"' !
Sollte es Schwierigkeiten bei der Übergabe von Zeichenausdrücken per Variable geben, so müssen ggf. die Ausführungszeichen in der Variablen enthalten sein und diese muß per Makrosubstition übergeben werden.
gcOleFile = '"' + 'HD:\Pfad\Datei.XLS' + '"'
.SaveAs(&gcOleFile)
True und False werden in Excel ausgeschrieben. Bei der Steuerung mit OLE sind jedoch .T. und .F. von Visual FoxPro zu verwenden, wohingegen in Wordbasic Wahr oder Falsch mit Hilfe der Numerischen Werte -1 bzw. 0 übergeben werden.
In Excel besteht die Möglichkeit, entweder mit der englischen oder der deutschen Version von VBA zu programmieren. Ich habe die englische Variante benutzt.
Es gibt Unterschiede im Verhalten sowie in der Befehlssyntax beim Erstellen des Objektes mit "Excel.Application" bzw. "Excel.Sheet"
oXLS=("Excel.Application") oXLS=("Excel.Sheet")
oXLS.Visible = .T. oXLS.Application.Visible = .T.
oXLS.range("B2").select oXLS.application. ;
range("B2").select
Beim Aufruf mit .Application muß die Arbeitsmappe (ActiveWorkbook) gesichert werden, bevor Excel verlassen wird, falls diese verändert wurde. Beim Aufruf mit .sheet ist eine Sicherung nicht notwendig, selbst wenn das Blatt (Sheet) geändert wurde.
Es folgen je ein Programmbeispiel mit Excel und Word. In den Beispielen habe ich noch Kommentare eingefügt, die auf Besonderheiten der jeweiligen Serveranwendung eingehen.
VFP schreibt Werte in Excel-Zellen und schreibt am Schluß das Ergebnis in ein Textfeld-Steuerelement einer VFP-Form.
* Programmcode aus OLE_XLS.PRG (gekürzt)
* -------------------------------------
set OleObject On* Registrierung von Win32Api Funktionen um VFP-Fenster
* später wieder den Focus zu geben, notwendig da der
* Focus auf dem Excel-Fenster nach dessen Aufruf ist,
* Achtung Unterschiede zwischen Windows 3.1,
* Windows 95 und Windows NT
DECLARE INTEGER GetActiveWindow IN win32api
* Win31DECLARE INTEGER SetFocus IN win32api ;
Integer nWinHandle
* WINNT und WIN95DECLARE INTEGER SetForegroundWindow IN win32api ;
Integer nWinHandle
* WindowHandle VFP-Fenster
pnFoxWindow = GetActiveWindow()
* pnFoxWindow = SetForegroundWindow()* Object erstellen
oXLS = createobject("Excel.application")
* Gebietskennung einstellen
= sys(3005,1033) && Englisch
* Arbeitsmappe hinzufügen
oXLS.Workbooks.Add* VFP - Fenster anordnen ....
* Excel-Fenster anordnen,
* Vorher Variablen für xl-Konstanten definieren,
* Fenster auf Maximum einstellen
* Hinweise !
* 1. Excel arbeitet mit Points statt mit Pixeln, deshalb muß
* die Fenstergröße in Excel ermittelt werden
* 2. Die xl- und vb-Konstanten aus Excel können nicht
* direkt verwendet werden. Stattdessen muß der Wert
* der xl-Konstanten übergeben werden.xlNormal = -4143
xlMaximized = -4137
oXLS.WindowState = xlMaximized* Breite und Höhe des Fensters ermitteln
pnWidth = oXLS.Width
pnHeight = oXLS.Height* Fenster Normal einstellen und dann auf Hälfte
* des Bildschirms justieren
* Hinweis !
* Die Eigenschaften von Fenstern wie .width, .height,
* .left und .top können nur geändert werden, wenn
* WindowState = xlNormal ist, nicht bei
* WindowState = xlMaximised.
With oXLS
.WindowState = xlNormal
.width = pnWidth/2
.height = pnHeight
Endwith* Workbook (Arbeitsmappe) Fenster justieren
oXLS.ActiveWindow.WindowState = xlNormal
With oXLS.ActiveWindow
.Top = 0
.Left = 0
.Width = oXLS.UsableWidth
.Height = oXLS.UsableHeight
Endwith* Objekt sichtbar machen
* Hinweis !
* Beim Start als OLE-Server wird Excel nicht
* angezeigt, es wird mit Hilfe von .Visible = .T.
* sichtbar gemacht.
oXLS.visible = .T.* Focus auf VFP-Fenster setzten
* WIN31
= SetFocus(pnFoxWindow)
* WINNT und WIN95
* = SetForegroundWindow(pnFoxWindow)* Programmcode aus cmdOLE.Click (gekürzt)
* ---------------------------------------* Werte der Form an Zellen
* in Excel übergeben
oXLS.range("A3").select
oXLS.activecell.value = "Wert 1"
.
.
oXLS.range("A7").select
oXLS.activecell.value = "Summe"
oXLS.range("B3").select
oXLS.activecell.value = thisform.txtText1.value
.
.
wait wind "Werte übergeben"
* Summe bilden
oXLS.range("B7").select
oXLS.activecell.value = "=sum(B3:B6)" && ! siehe Zelle
wait wind "Summe gebildet"* Formatierung von Zellen
* Zahlen Format
oXLS.Range("B3:B7").select
oXLS.selection.NumberFormat = "##,#0.00"* Ränder und Schrift Fett
* Vorher Variablen für xl-Konstanten definieren
xlTop = -4160
xlMedium = -4138
oXLS.Range("A7:B7").select
oXLS.selection.Borders(xlTop).weight = xlMedium
oXLS.selection.Font.Bold = .T.
wait wind "Zellen Formatiert"
* Summe an Form übergeben
oXLS.Range("B7").select
thisform.txtText5.value = oXLS.ActiveCell.Value
wait wind "Summe an Form übergeben"
* Programmcode aus cmdQuit.Click (gekürzt)
* ----------------------------------------* XLS-Datei sichern
* Hinweis !
* Vor Abspeichern muß eine evt. vorhandene Datei
* gleichen Namens gelöscht werden, da Excel sonst auf
* eine Sicherheitsabfrage vor dem Absichern besteht.pcFile = sys(5)+sys(2003)+"\OleFile.Xls"
If file(pcFile)
delete file &pcFile
Endif
pcFile2 = '"'+pcFile+'"'
oXLS.ActiveWorkBook.SaveAs(&pcFile2)* Objekt beenden
oXLS.quit
Das obige Beispiel ist etwas albern und hat keinen praktischen Wert. Die dort ausgeführte Aufgabe hätte man nur mit VFP schneller und einfacher erledigt. Das Beispiel soll nur die Möglichkeiten der OLE-Automation mit Excel als OLE-Server aufzeigen.
Beispiel mit MS Word (6.0)
Dieses Beispiel hat einen größeren praktischen Bezug. Es zeigt im Ansatz, wie man eine Serienbriefanwendung realisieren kann. Die Datenfelder werden über einen Suchvorgang mit den Suchworten @Firma1, ... etc. ausgetauscht. Eine andere Möglichkeit, die Datenfelder aus der Adressdatei einzufügen, besteht im Anspringen von Textmarken. Die Tabelle Ole_Word.Dbf mit den Adressen und die Wordvorlage Ole_Word.Dot befinden sich im gleichen Verzeichnis wie das Beispielprogramm.
* Programmcode aus frmOle_Word.Init (gekürzt)
* -------------------------------------------* _screen ausblenden
_screen.left = -sysmetric(1)* OLE-Server Objekt erstellen
oWord = createobject("Word.Basic")* Gebietskennung einstellen
* Bei Word ohne Einfluß
= sys(3005,1031) && Deutsch* Wordfenster maximieren
* Rückgabewert -1 maximiert,
* 0 nicht maximiert
If oWord.AnwMaximieren = 0
oWord.AnwMaximieren
Endif* Programmcode aus cmdOLE.Click (gekürzt)
* ---------------------------------------* Dokumentvorlage - .Dot-Datei festlegen
cWordDot = '"' + sys(5) + sys(2003) + "\OLE_WORD.DOT"
+ '"'* Sicherungsdatei festlegen
cSichernUnter = sys(5) + sys(2003) + "\OLE_WORD.DOC"
cSichernUnter2 = '"' + cSichernUnter + '"'* Für jeden Brief neue Dokumentvorlage laden,
* in der Methode "Ersetzen" werden die Suchwörter
* gegen die Datenfelder ersetzt
Do While NOT eof()oWord.Dateineu(&cWordDot)
thisform.Ersetzen("Firma1")
thisform.Ersetzen("Firma2")
thisform.Ersetzen("Name")
thisform.Ersetzen("Strasse")
thisform.Ersetzen("PLZOrt")
oWord.BeginnDokument()* Parameter 0 für Drucken im Vordergrund !
* Wichtig ! Wenn im Hintergrund gedruckt wird, wird
* Word geschlossen und Druck dabei abgebrochen.
oWord.DateiDrucken(0)
skipIf NOT eof()
wait wind "Nächste Adresse"
thisform.refresh
Else
wait wind "Schluß"
Endif* Dokument sichern und schließen
If File(cSichernUnter)
delete File &cSichernUnter
Endif
oWord.DateiSpeichernUnter(&cSichernUnter2)
oWord.DateiSchließenEnddo
thisform.release
* Code aus frmOle_Word.Destroy
* ----------------------------* _screen wieder einblenden
_screen.left = 0
release oWord
clear events* Code aus frmOle_Word.Ersetzen (gekürzt)
* ---------------------------------------parameters cField
cSuchen = ""
cErsetzen = ""Do Case
Case cField = "Firma1" or cField = "Firma2"
cSuchen = '"@' + cField + '"'
cErsetzen = '"' + alltrim(Ole_Word.&cField) + '"'
*
Case cField = "Name"
cSuchen = '"@' + cField + '"'
*
Do case
Case NOT empty(Ole_Word.Anrede) AND ;
empty(Ole_Word.Vorname)
cErsetzen = '"' + alltrim(Ole_Word.Anrede) + ;
" " + alltrim(Ole_Word.&cField) + '"'
*
.
.
Endcase
*
.
.
Endcase
oWord.BearbeitenErsetzen(&cSuchen, ;
&cErsetzen,0,0,0,0,0,.F.,.T.,.F.,0,0)
Soweit die beiden Beispiele. Diese wurden von mir sichtbar dargestellt und mit den "wait windows"-Befehlen versehen, um die Anschaulichkeit zu erhöhen. Zu beachten ist noch, daß diese von mir bis jetzt nur unter Windows 3.11 ausgetestet wurden. Unterschiede im Verhalten unter Windows 95 oder Windows NT sind nicht auszuschließen. Hinzu kommen Unterschiede im Verhalten der verschiedenen Word- bzw. Excel-Versionen (ab 6.0 bzw. 5.0 aufwärts). Und zuguterletzt VFP selbst. Hier hat mich die Version 3.0b schon eingeholt. Wärend ich bei der Version 3.0 Zeichenausdrücke noch per Makrosubstition übergeben mußte (s.o.) hatte sich dieses Problem bei der Version 3.0b schon erledigt.
Weitere Informationen zur OLE-Automation kann man unter anderem an folgenden Stellen erhalten:
Klaus Gerhardt erreichen Sie per eMail unter 100326.2575@compuserve.com oder im dFPUG-Forum in CompuServe.