Session D-OLE

OLE-Server in der Praxis

Patrick E. Schärer
Business & System


Aktuelle Schlagworte wie „3-tier“, „COM“, OLE-Server“ lassen einige Fo’ler (zum Teil nach den ersten Versuchen) einen großen Bogen machen, zumindest für ihre praktische Entwicklungsarbeit. Aber es lohnt sich, die anfänglichen Klippen eines solchen Programms ohne User-Interface zu überwinden. Diese Session richtet sich an Entwickler, die in die Arbeit von VFP als COM-Server eingeführt werden wollen, und das ganze praktisch bei sich zum Laufen kriegen wollen. Sie richtet sich nicht an ausgefoxte VFP-Profis, die noch das letzte aus ihrem fertigen COM-Server heraus-tunen wollen... Sie sollten jedoch Grundbegriffe der Objektorientierten Programmierung (den Umgang mit Klassen und Objekten) etwas kennen. 

Wozu COM / 3-tier-architecture?

Wenn Sie ihre Anwendung als herkömmliche Desktop Anwendung konzipieren, liegt alle Logik, mit der Sie auf Daten zugreifen im gleichen Programm wie auch die Oberfläche, bei der Sie sich Mühe geben dem Anwender eine angenehme Benutzerschnittstelle zu liefern. Dies ist die herkömmliche Client/Server-Strategie. Sie besteht also aus zwei Ebenen: Server auf der einen Seite (Daten) und Client auf der anderen (Logik und Benutzeroberfläche) 

Ihr Nachteil besteht einfach darin, dass die einzige Möglichkeit, um die von Ihnen programmierte Logik zur Be- und Verarbeitung bzw. Abfrage der Daten eben über genau diese ebenfalls von Ihnen hergestellte Benutzeroberfläche läuft. Sie können zwar über ODBC auch von einer anderen Anwendung aus auf die Daten zugreifen, nicht jedoch unter Verwendung Ihrer Geschäftslogik, denn die ist eins mit ihrer Oberfläche. 

Die Technik, von der wir in dieser Session sprechen wollen, trennt nun die Benutzeroberfläche Ihres Programmes (des „Client“, nach Client/Server-Gesichtspunkt gesehen) völlig ab. Was bleibt übrig? Lediglich alles, worin die eigentliche Geschäftslogik besteht. Andersherum gesprochen: Die Benutzeroberfläche wird getrennt von der Geschäftslogik. Das bedeutet:

Sie mögen vielleicht sagen: „Aber für meine Anwendung mache ich die Oberfläche selbst und ich mache sie in VFP“? Trotzdem! Wenn Sie die Anwendung von vornherein nach dem 3-tier Modell strukturieren, haben Sie nach vorne hin größtmögliche Offenheit, insbesondere falls die Anwendung einmal in einen Internetserver integriert werden soll. 

Wie arbeitet COM allgemein?

Um es anderen Anwendungen als VFP möglich zu machen, auf Ihre VFP-Anwendung und deren Methoden zuzugreifen, eventuell sogar aus Ihrer Anwendung Werte zurückgeliefert zu bekommen und diese weiterzuverarbeiten, machen wir uns das COM-Modell zunutze, das Windows uns liefert. 

COM steht für „Common Object Model“ oder auch “Component Object Model”.

Das bedeutet: wenn eine Klasse als OLE-Public registriert worden ist (wie das von uns gemacht wird sehen wir unten) kann in einer anderen Anwendung eine Objekt aus Ihrer VFP-Klasse erzeugt werden und von dort aus auf die Methoden und Eigenschaften dieser Klasse zugegriffen werden. Die meisten Entwickler haben ja schon mal Word aus VFP aus angesprochen:

Da die eigenen Microsoft-Anwendungen immer als OLE-Server konzipiert sind (sie können also anderen Anwendungen "dienen"), können wir sie gut als Beispiel heranziehen. So wie wir im folgenden Beispiel Word als COM-Server von VFP aus ansprechen, werden wir später unsere eigene VFP-Anwendung von Word oder einem anderen Client aus ansprechen, sie wird damit selbst zum COM-Server.

Zusammenfassend: COM ist also eine allgemein in Windows zur Verfügung stehende Technik, mit der wir unsere Anwendung Windows-öffentlich (also für andere Anwendungen) zur Verfügung stellen können. Dabei kann der Client, mit der wir diese Klasse ansprechen natürlich auch wiederum ein VFP-Client sein.

COM arbeitet mit der OLE-Technik zur Verknüpfung von Programmen. Wir können also statt COM-Server auch von einem OLE-Server sprechen (gleichbedeutend). 

Einfaches Beispiel eines COM-Servers

Aufgabenstellung des Beispiels

Unser OLE-Server soll folgende Aufgabe erfüllen: Wenn er von irgendeinem Programm, z.B. von Word aus, aufgerufen wird, soll er die Möglichkeit geben, einer bestimmten Methode einen Parameter zu übergeben mit einer Adress-Nummer. Wird diese Adresse in der Adresstabelle gefunden, wird die Adresse als String von der Methode zurückgegeben und kann so z.B. in ein Word-Dokument eingefügt werden. Der Anwender würde also in diesem Falle nichts davon merken, dass hier ein FoxPro-OLE-Server gestartet wird, sondern würde lediglich feststellen, dass die von ihm angeforderte Adresse eingefügt wurde.

Um dieses Beispiel selbst nachzuvollziehen, legen sie sich einen neuen Ordner an, darin ein neues leeres Projekt. Legen Sie darin eine einfache Adress-Tabelle (freie Tabelle genügt für das Beispiel) mit typischen Feldern an: "Vorname", "Name", "Strasse", "Plz", "Ort" (alles als Zeichenfelder) und zusätzlich ein ID-Feld "ID" (Integer)

Das Herzstück: eine „öffentliche“ Klasse

Bei objektorientiert konzipierten Anwendung haben wir bereits kaum noch etwas mit PRGs zu tun, das meiste liegt in Objekten. Trotzdem haben wir aber immer noch zumindest ein prozedurales Main-Programm verwendet, um das Anwendungsobjekt zu instanziieren und nach Beenden des Programmes letzte Aufräumungsarbeiten durchzuführen.

Dies ist hier nicht mehr der Fall.

Wir verwenden eine Anwendungsklasse, die von außen (ohne Verwendung eines Hauptprogrammes) instanziiert wird.

Darin besteht also das Starten des Ole-Servers: nicht im Starten einer EXE, die als erstes ihr Hauptprogramm ausführt, sondern im unmittelbaren instanziieren der Anwendungsklasse, die wir nun programmieren (aus diesem Grund muss seit VFP6 ein Projekt keine Hauptdatei mehr haben, wenn es als OLE-Server kompiliert wird). Zuerst werden wir nun diese Klasse entwerfen, als eine übliche VFP-Klasse, dann werden wir sehen, wie wir diese Klasse der Windows-„Öffentlichkeit“ zur Verfügung stellen.

Anlegen der Anwendungs-Klasse

Wir legen eine neue Klasse an aus der Basisklasse „Custom“. Wir können ihr schlicht den Namen „App“ (bitte nicht „application“ – dies ist ein geschützter Name) geben. Bei „Speichern in“ geben wir als Klassenbibliotheksnamen den einer neuen Klassenbibliothek wie MyOleServer an. 

Diese neue „App“-Klasse muss weder im Init- noch im Destroy-Event spezielle Operationen ausführen. Unser ganzes Interesse gilt der Methode, die eine Adressnummer in Empfang nimmt und eine Adresse als Zeichenkette zurückgibt:

Methode zum Ermitteln der Adresse

Legen wir also eine neue Methode an, z.B. mit dem Namen „AdrGet“. Der Code könnte etwa lauten: 

1 Ein sehr wichtiges Stück Code, das Sie bei allen OLE-Servern benötigen werden! Sobald Sie eine Anwendung von außen als OLE-Server starten, ist das Default-Verzeichnis immer das der VFP-Runtime, also meist das Windows\System32-Verzeichnis. Zu Anfang müssten sie also wechseln in das Verzeichnis des Programmes das aktuell läuft. Wir ermitteln es mit SYS(16) und schneiden den Namen der VCX (hinten) mit JUSTPATH() ab und den Namen der aktuell laufenden Methode vorne mit SUBSTR(.... AT(":",...)-1).

Ansonsten bedarf der Code vermutlich keiner weiteren Erklärung. Der Rückgabewert lcReturn wäre nun der Wert der nicht nur in die aufrufenden VFP-Methode zurückgegeben wird, sondern heraus aus VFP über OLE an das aufrufende Programm.

Wie wird diese Klasse „öffentlich“?

Damit unsere App-Klasse später von Windows-Anwendungen angesprochen werden kann, ist hier der erste Schritt, dass wir die spezifische Klasse als „OLE Public“ definieren. Rufen Sie dazu im Menü Klassen die Klasseninfo auf und klicken dort die Checkbox „OLE Public“ an.

Testen der Klasse unter VFP

Sie sollten sich vor dem Erproben Ihrer Klasse in der „Windows-Öffentlichkeit“ erst davon überzeugen, dass diese "lokal" einwandfrei funktioniert, da die außerhalb von FoxPro zurückgegebenen Fehlermeldungen meist so kryptisch sind, dass man damit bei der Fehlersuche nicht viel anfangen kann. 

Dazu legen wir ein Objekt aus unserer Klasse an und testen die Methode AdrGet:

Es müsste eine Zeichenkette mit der Adresse für ID=1 ausgegeben werden. Wenn die Klasse hier einwandfrei funktioniert, können wir fortfahren:

Kompilieren und Registrieren der DLL

Sie können einen OleServer sowohl als DLL als auch als EXE kompilieren. Der grundsätzliche Unterschied liegt darin, dass eine DLL als "in-process"-Server arbeitet, d.h. im gleichen Process läuft, wie das Programm, das sie aufrief. Ein in-process Server kann nicht über eine Benutzeroberfläche verfügen. Kompilieren Sie ihren Server hingegen als EXE, so ist dies ein out-of-process Server. Bei jeder Instanz des Servers öffnet sich also ein eigener, unabhängiger Task. Out-of-process Servers können über eine Benutzeroberfläche verfügen, z.B. Forms anzeigen. 

Kompilieren

Der Dialog zum Kompilieren unterscheidet sich von VFP6 zu VFP6SP3 (Service Pack 3):
Erstellungs-Dialog unter VFP 6 bis SP2: Sie können neben der APP auswählen zwischen EXE und COM-DLL

Erstellungsdialog unter VFP 6 mit Service Pack 3 (neue Funktionalität, aber unliebsame abgeschnittene Zeilen. Gemeint ist bei 3. Option „EXE“ und bei 4. und 5. Option „DLL“)

 


Die im Service-Pack 3 eingeführte zusätzliche Möglichkeit eines „Multi-Thread-COM-Servers“ läßt eine Problematik des früheren COM-Modells bei VFP erkennen. Dort konnte eine gestartete DLL im Speicher nur einen Thread (also ein daraus erzeugtes COM-Objekt) verwalten. Der Multi-Thread-COM-Server erlaubt einer DLL im Speicher, mehrere COM-Objekte zu verwalten. Dies ist natürlich generell von Vorteil. Für Absturz-gefährdete COM-Server macht jedoch nach wie vor die Verwendung von Single-Thread-COM-Servern Sinn. In diesem Zusammenhang beinhaltet das Service-Pack 3 auch eine neue Laufzeitbibliothek (VFP6T.DLL).  Beachten Sie auch hierzu die Hinweise der Hilfedatei VFPSP3.CHM.

Die VBR-Datei

Beim Kompilieren wurde eine Datei mit der Endung .VBR erzeugt. Sie enthält die Schlüssel, die VFP in die Registry einträgt, wenn es unsere Anwendung dort registriert. Wollen sie Schlüssel verändern oder hinzufügen, können Sie dies in der VBR-Datei tun vor einem erneuten Registrieren.

Registrieren

Alle OLE-Anwendungen greifen immer auf die Registrierungsdatenbank von Windows zu. Was dort nicht eingetragen ist, ist gewöhnlich auch per OLE nicht von anderen Anwendungen ansprechbar. Zwar wird ab Service Pack 3 von Visual Studio die Registrierung automatisch beim Kompilieren einer COM-DLL/EXE durchgeführt. Trotzdem will ich hier den einfachen Vorgang der manuellen Registrierung kurz zeigen:  Verkleinern Sie VFP und alle anderen Anwendungen zum Symbol, so dass Sie den Desktop sehen (Tip: Windows95-Taste + M). Erstellen Sie auf dem Desktop eine Verknüpfung mit der REGSVR32.DLL aus dem SYSTEM32-Pfad Ihres Windows/WinNt-Ordners (Wechseln in diesen Ordner, Suchen der entsprechenden Datei, rechts klicken, auf den Desktop ziehen, Verknpüfung hier erstellen?  anklicken).

Öffnen Sie ein Fenster Ihres Anwendungs-Ordners und ziehen Sie die von Ihnen erstellte AdressInfo.Dll bzw. ADRESSINFO.EXE auf das Symbol der REGSVR32.EXE.

Sie müssten als Ergebnis eine Meldung bekommen wie diese:

Nun ist Ihre DLL registriert und kann von anderen OLE-fähigen Windows-Programmen verwendet werden.

Verwenden der DLL von außen (Test)

Zum ersten Test wollen wir die DLL innerhalb von VFP, aber „von außen“ als DLL ansprechen. Dies funktioniert genau so wie wir es schon beim Word-Objekt gesehen haben:  

Eine Klassenbibliothek muss (und kann) hier nicht angegeben werden: Die „Klassenbibliothek“ ist gewissermaßen die Registry, in der sämtliche vorhandene Klassen registriert sind. Stößt also VFP bei der Klassendefinition auf einen Punkt, wird die Klasse nicht mehr im aktuellen VFP sondern in der Registry gesucht. Nun kann das neu erzeugte COM-Objekt verwendet werden:  

? oAdrInfo.AdrGet(2)

Es müsste eine Adresse auf dem Bildschirm ausgegeben werden, wie zuvor im FoxPro-internen Test.

Beispiel für die Verwendung einer Visual FoxPro-COM-DLL 
außerhalb von VFP (hier: aus Word)

Wir wollen in einem Beispiel testen, wie wir nun unsere VFP-DLL außerhalb verwenden können. Im Beispiel soll der Anwender mit Word arbeiten und wenn er einen bestimmten Kunden einfügen will auf F3 drücken. Die Adresse wird eingefügt.

Legen Sie dazu in Word ein neues Makro an (Extras | Makros) mit der Tastatur-Zuordnung F3. Das Makro könnte so lauten (Achtung: kein VFP sondern VBA-Code!)[1]

 

In diesem Beispiel müsste der Anwender zuerst einen Namen schreiben und markieren. Diese Markierung wird dann als Name verstanden und in unserem VFP-Server gesucht. Wird er gefunden, so wird die Markierung ersetzt durch die komplette Adresse.

1 In VBA (Visual Basic for Applications) werden Variablen so angelegt. Apostroph oben zeigt einen folgenden Kommentar an (entspricht in VFP entweder * oder in der gleichen Zeile && ).

2 Objektvariablen werden mit SET zugewiesen.

3 OLE-Klassename.

4 In Selection.Text befindet sich die aktuelle Markierung als String. Diese wird übergeben an unseren COM-Server.

Keine Benutzeroberfläche?

Der entscheidende Unterschied eines middle-tier OLE-Servers zu einem herkömmlichen Programm ist, dass der COM-Server ohne User-Interface arbeitet. Stößt ein OLE-Server auf einen Befehl, der eine Benutzer-Interaktion anfragt, läuft er auf einen Fehler, wie:

Sie müssen also dafür sorgen, dass jegliche Meldungen, Messageboxen, WAIT WINDOWs etc. ausbleiben! Da Sie aber zum Testen Ihrer Anwendung, sie durchaus zuerst unter VFP starten und dort ggf. Meldungen wünschen, empfehle ich folgende Arbeitsweise.

1.        Stellen Sie mit Hilfe der application.startmode-Eigenschaft fest, ob die Anwendung gerade als Server gestartet wurde.

2.        Setzen Sie eine Eigenschaft (z.B. lServer) Ihrer Anwendungsklasse entsprechend.

3.        Setzen Sie alle Vorgänge, die eine User Interaktion benötigen in eigene Methoden und fragen da jeweils ab, ob lServer auf .T. steht. Ist dies der Fall, lassen Sie die Meldung weg bzw. setzen einen Standard-Wert.

Um festzustellen, wie die Anwendung gestartet wurde, stellt uns VFP seit SP3 die application.startmode Eigenschaft. Sie kann folgende Werte haben. 

Value

Description

0

Interaktiver Modus (gestartet in VFP / bei der Entwicklung)

1

Visual FoxPro wurde gestartet als Application-Objekt mit:

oMyObject = CREATEOBJECT('VisualFoxPro.Application')

2

Anwendung gestartet als out-of-process .exe automation server.

3

Anwendung gestartet als in-process .dll automation server.

4

Anwendung gestaret als unabhängige oder .app or .exe file.

5

Anwendung gestartet als multithreaded in-process .dll automation server.

 

Demnach könnte die Abfrage lauten:  

Debugging

Sie sollten unbedingt darauf achten, dass jeder OLE-Server zuerst reiflich IN VFP getestet wird, also noch nicht als Server. Nur da können Sie den Code durch-tracen um gezielt Fehler zu suchen. (Ich spreche hier nicht von Neuerungen, die es in VFP7 geben wird, die werden uns andere Sessions vorgestellt, sondern gehe auf die praktische Anwendung ein – und die muß noch über einige Zeit über VFP7 verzichten).

Erst wenn dies gründlich erfolgreich war, sollten Sie sich daran machen, Ihren OLE-Server als Server anzusprechen von außen (selbst wenn dieses "außen" wiederum VFP ist).

Nun entsteht das Problem, wenn jetzt Fehler auftauchen, dass diese nur extrem schwierig zu finden sind, da die Fehlermeldungen extrem kryptisch sind, und der Code nicht im Step-Modus durchlaufen werden kann.

Um diesem Problem ein wenig zu Leibe zu rücken, stellt uns SP3 von VFP6 eine wichtige neue Funktion zur Verfügung: COMRETURNERROR. Sie bewirkt, dass eine Fehlermeldung bis an den OLE-Client weitergegeben wird. Wenn Sie zuvor feststellen, ob sich die Anwendung gerade im Servermodus befindet (s.o.), könnte der code folgendermaßen aussehen:  

 Anstelle User-Interaktion

Um dem COM-Server Befehle in Auftrag zu geben, wird auf jeden Fall eine Anwendung nötig sein, die diese Befehle ausführt. Die anderer Nutzen des nicht mehr verwendeten User-Interfaces ist die Ausgabe von Meldungen. Dies kann ersetzt werden durch eine Log-Datei. Es empfiehlt sich, eine eigenes LOG-Objekt anzulegen, das dies verwaltet.


[1] Vorhanden im Beispielcode der DevCon-CD: "Makro.txt"

[2] Beachten Sie, daß In-process Server (DLL's) immer das User-Interface abschalten (und bei Befehlen, die eine User-Interaktion erfordern einen Fehler generieren. Bei Out-of-Process Servern (EXE's) dagegen muß explizit die Benutzeroberfläche abgeschaltet werden (SYS(2335,0)=unattended Server Mode), wenn keine Benutzeroberfläche gewünscht ist, wie z.B. bei einem Internet-Server.

dFPUG c/o ISYS GmbH

Frankfurter Str. 21 b

 

D-61476 Kronberg

per Fax an:

+49-6173-950903

oder per e-Mail an:

konferenz@dfpug.de

© Texte, Grafiken und Inhalt: ISYS GmbH