Die API-Programmierung ist ein Bereich von Visual FoxPro, der vielen Entwicklern immer wieder Kopfschmerzen bereitet. Völlig zu unrecht, wie ich meine, denn eigentlich ist die API-Programmierung nicht so unterschiedlich von anderen Programmiertechniken. In dieser Session wird die Nutzung der von Windows bereitgestellten API, der Win32 API, behandelt, nicht aber die Erstellung einer eigenen API, die in der Tat sehr viel komplexer ist und C/C++ Kenntnisse voraussetzt.
API steht für Application Programming Interface. Im Prinzip handelt es sich um eine Sammlung von Funktionen, die eine Anwendung, in diesem Fall Windows, einer anderen zur Verfügung stellt. Das ist in diesem Fall eine Visual FoxPro-Applikation. Eine API ist eine Bibliothek von Funktionen, vergleichbar mit den in Visual FoxPro bekannten FLL-Bibliotheken, oder aber auch mit denen, die Sie als Prozedurdatei selbst geschrieben haben. Typischerweise werden API's in DLL oder EXE Dateien gespeichert, die im Windows/System-Verzeichnis abgelegt werden und von Visual FoxPro geladen werden können.
Windows bietet tausende von Funktionen an, die alle Bereiche abdecken, die in Windows nur denkbar sind. Das fängt bei einfachen Dingen, wie Fenster- und Menühandling an, geht über Dateisysteme und Netzwerkfunktionen, bis hin zu komplexeren Dingen, wie Sicherheitssysteme, ODBC, Multimedia und Internetansteuerung. Von diesen Funktionen können Sie den Großteil in Ihren Applikationen verwenden.
Zusätzlich bieten viele Hersteller weitere API-Bibliotheken an, zum Beispiel um Daten zu packen, auf OLAP-Datenbanken zuzugreifen, und vieles mehr. Die Nutzung dieser API-Funktionen unterscheidet sich nicht von der Benutzung der Windows-API. Die einzige zusätzliche Voraussetzung ist in den meisten Fällen nur, daß Sie die DLL-Datei mit Ihrer Applikation ausliefern, da diese im Gegensatz zu Windowsbibliotheken meist nicht auf dem Rechner Ihrer Kunden installiert ist.
Wesentlich für das Verständnis von API Funktionen ist das Verständnis von Datentypen. Wie die meisten Visual FoxPro Funktionen erwarten API-Funktionen Parameter, die einen ganz bestimmten Typ haben müssen. Sie können einer Funktion, die eine Zeichenkette erwartet, nicht ein Datum übergeben. Das ist bei API-Funktionen nicht anders als bei Visual FoxPro-Funktionen. Unterschiedlich sind dagegen aber zwei Dinge. Zum einen verwenden API-Funktionen andere Datentypen als Visual FoxPro, und zum anderen reagiert eine API-Funktion auf einen falschen Parameter nicht mit einem Syntax-Fehler, sonder unter Umständen mit einem Systemabsturz. Letzteres sollte Sie aber nicht davon abhalten, API-Funktionen zu nutzen, denn wenn Sie den Datentyp wissen und korrekt übergeben, stürzt das System auch nicht ab.
Bevor Sie eine API-Funktion in Visual FoxPro verwenden, müssen Sie Visual FoxPro mitteilen, wie die Funktion heißt, wo sie sich befindet und welche Parameter ihr übergeben werden müssen. Da es keine eingebaute Funktione ist, kann Visual FoxPro dies nicht selbständig wissen. Diese Bekanntgabe nennt sich Deklaration, ähnlich heißt auch der Befehl: DECLARE. Verwechseln Sie diesen Befehl nicht mit dem DECLARE-Befehl, mit dem Sie Arrays definieren können und der ein Synonym für DIMENSION ist. In der Hilfe finden Sie DECLARE unter dem Eintrag "Declare - DLL". Dieser Befehl hat folgende Syntax:
DECLARE [cFunctionType] FunctionName IN LibraryName [AS AliasName]Zwei Parameter müssen immer angegeben werden: FunctionName und LibraryName. Ersterer ist der Name der API-Funktion, die Sie verwenden möchten. Ungewöhnlich für Visual FoxPro-Entwickler ist am Anfang meist, daß diese Name exakt in Groß- und Kleinschriebung stimmen muß. Namen von API-Funktionen sind Case-Sensitive. Dies hat historische Gründe, da früher die meisten API-Bibliotheken in C geschrieben wurden und in dieser Sprache zwischen Groß- und Kleinschreibung unterschieden wird. Ein solche Name ist beispielsweise "FindWindow". Wenn Sie stattdessen "FINDWINDOW" oder "findwindow" verwenden, erhalten Sie spätestens beim Aufruf der Funktion eine Fehlermeldung.
Anstelle von LibraryName geben Sie den vollständigen Pfad und Namen der Datei an, in der sich die API-Funktion befindet. Diese Information können Sie bei Zusatzbibliotheken meist der Beschreibung entnehmen oder Sie finden Sie im Fall von Windows-Funktion in der Beschreibung der Funktion. Wenn Sie nicht den vollständigen Pfad angeben, versucht Visual FoxPro die Datei im System-Verzeichnis und im eingestellten MS-DOS-Pfad zu suchen. Für die häufig benutzten Windows-Funktionen gibt es eine Abkürzung. Windows-Funktionen werden in verschiedenen Dateien gespeichert, dies sind Kernel32.dll, Gdi32.dll, User32.dll, Mpr.dll, und Advapi32.dll. Sie müssen aber nicht wissen, in welcher dieser Dateien genau sich die Funktion befindet, sondern können stattdessen als Name der Bibliothek "Win32API" angeben. Dann sucht Visual FoxPro in den oben aufgeführten Dateien automatisch nach dieser Funktion.
Wenn Sie eine API-Funktion deklariert haben, steht Ihnen diese Standardmäßig unter dem gleichen Namen zur Verfügung, wie er auch in der Bibliothek verwendet wird. Wenn Sie also beispielsweise mit dem folgenden Befehl die Funktion FindWindow() deklarierten:
Declare Integer FindWindow in Win32API String, StringKönnen Sie diese Funktion in Ihrem Programm mit FindWindow(cString1,cString2) aufrufen. Bei der Verwendung im FoxPro-Quellcode ist es übrigens unerheblich, wie Sie den Funktionsnamen schreiben, Sie können auch FINDWINDOW(cString1,cString2) verwenden. Die Groß- und Kleinschreibung muß nur beim DECLARE-Befehl berücksichtigt werden. Manche Funktionsnamen sind sehr lang und unter Umständen wollen Sie einen kürzeren Namen in Ihrem Programm verwenden. Ähnlich wie bei Tabellen können Sie bei API-Funktionen einen Alias vergeben. Der folgende Befehl etwa
Declare Integer FindWindow in Win32API AS SucheFenster String, StringErlaubt es Ihnen die Funktion mit SucheFenster(cString1,cString2) aufzurufen. Auch wenn ein Alias auf den ersten Blick verlockend scheint, möchte ich Ihnen dennoch von der Verwendung abraten. Wenn Sie später den Quellcode lesen ist der Funktionsaufruf oft weit entfernt von seiner Deklaration. Wenn Sie nun nachschauen möchten, was díe API-Funktion macht, können Sie sofort in der Windows-Hilfe nachschlagen, sofern Sie den Originalnamen übernehmen. Auch für andere Entwickler ist der Code leichter lesbar, insbesondere wenn sie diese Funktion bereits kennen.
In den beiden Deklarationen habe ich bereits weitere Bestandteile verwendet. Direkt auf das Schlüsselwort DECLARE folgt der Datentyp des Rückgabewertes. Wie bereits oben erwähnt, verwenden API-Funktionen andere Datentypen. In Visual FoxPro sind dies String (Character), Zahlen (numerisch), Datum, etc. Die Windows-API dagegen unterscheidet sehr viel genauer zwischen den einzelnen Typen. So gibt es beispielsweise zwei Typen von Zeichenketten, und zahlreichen numerische Typen. Im einzelnen unterstützt Visual FoxPro folgende Datentypen:
Integer und Long: Dies sind Ganzzahlen, die Werte zwischen -2147483648 und 2147483647 annehmen können. Diese Zahlen sind 32 bit oder 4 Byte groß und können keine Nachkommastellen enthalten. In vielen Dokumentationen werden Sie solche Zahlen im Hexadezimalformat finden. Auch unter Visual FoxPro können Sie solche Zahlen verwenden, indem Sie ihr 0x voranstellen. So entspricht etwa die Zahl 0xFF dem dezimalen Wert 255.
Die meisten API-Funktionen, die Zahlen erwarten, erwarten Integer-Zahlen unter den verschiendensten Bezeichnungen. Wenn Sie Beschreibungen von API-Funktionen lesen, werden Sie etwa auf Bezeichnungen wie diese treffen: UINT, DWORD, BOOL, VOID, HANDLE, HWND, HDC, HINSTANCE. Alle diese Datentypen sind eigentlich Integer-Typen.
Short: Nur wenige API-Funktionen verwenden diesen Datentyp. Es handelt sich ebenfalls um eine Ganzzahl, nur kann dieser Datentyp nicht so große Werte aufnehmen. Seine Wertebereich geht von -32768 bis 32767.
Single: Hierbei handelt es sich um eine sogenannte Fließkommazahl. Das sind ganz normale numerische Werte, die Nachkommastellen enthalten können. Im Vergleich zu den numerischen Werten bei Visual FoxPro kann Single nur eine begrenzte Nachkommazahl abbilden.
Double: Dies entspricht den numerischen Werten von Visual FoxPro. Daten dieses Types können jegliche Art von Zahlen mit einer hohen Genauigkeit aufnehmen, auch wenn diese Nachkommastellen enthalten. Double wird wie auch Single und Short in API-Funktionen relativ selten verwendet, nämlich dann, wenn Nachkommastellen auch benötigt werden. Dies ist zum Beispiel der Fall, wenn Berechnungen durchgeführt werden sollen.
String: Entspricht einem ganz normalen Visual FoxPro-String. In der API-Programmierung kennzeichnet CHR(0) das Ende eines Stringes. Deswegen können Sie diesen Wert nie in einem String verwenden, der von einer API-Funktion ausgewertet werden soll. Wenn Sie einen String an einer API-Funktion übergeben, hängt Visual FoxPro immer ein CHR(0) an den String an. Da dieser aber nach dem Aufruf wieder entfernt wird, brauchen Sie sich darum nicht zu kümmern.
Wenn Sie diese Liste mit den Datentypen von Visual FoxPro vergleichen, werden Sie feststellen, daß einige bei API-Funktionen fehlen. Diese werden in der API-Programmierung meist anders abgebildet. So sind beispielsweise logische Werte immer von Typ Integer. Ein Wert von 0 bedeutet .F., jeder andere Wert bedeutet .T. Ein Datum wird bei der Windows-API Programmierung auf verschiedene Weisen realisiert. Einige Funktionen erfordern, daß Sie das Datum als Anzahl von Tagen seit einem bestimmten Datum übergeben. Andere erfordern umfangreiche Strukturen. Objekte können Sie an API-Funktionen überhaupt nicht übergeben. Auch Arrays werden nicht direkt akzeptiert, sondern müssen konvertiert werden. Dazu erfahren Sie später noch mehr.
Im Gegensatz zu Visual FoxPro-Funktionen ist es bei API-Funktionen von entscheidender Bedeutung, ob ein Parameter als Wert oder als Referenz übergeben wird. Bei der Übergabe als Wert erhält die API-Funktion nur den Wert, den der Parameter zum Zeitpunkt des Aufrufes hat. Sie können hier Variablen, Zahlen oder auch Ausdrücke verwenden. Die API-Funktion hat keine Möglichkeit, eine übergebene Variable zu ändern. Bei der Übergabe als Referenz dagegen wird die Variable direkt übergeben. Wenn die API-Funktion den Wert des empfangenen Parameters ändert, ändert sich auch der Inhalt der Variablen. Sie können der für die Übergabe als Referenz nur eine Variable angeben. Typischerweise werden Übergaben als Referenz genutzt, wenn die API-Funktion darin einen Wert zurückgeben möchte. Da eine Funktion nur einen Wert zurückgeben kann, wird diese Technik oft genutzt, wenn die API-Funktion mehr als einen Wert zurückgeben möchte.
Um einen Parameter als Wert zu übergeben, müssen Sie im DECLARE-Befehl nur seinen Typ angeben:
DECLARE APIFunktion in Win32API IntegerDiese Zeile deklariert also eine Funktion, die keinen Rückgabeparameter hat und deren einziger Parameter eine Ganzzahl ist, die nicht verändert wird. Sie können diese Funktion also mit APIFunktion(nVar) oder mit APIFunktion(4) aufrufen. Wenn die Funktion dagegen die Übergabe als Referenz erfordert, sieht die Deklaration wie folgt aus:
DECLARE APIFunktion in Win32API Integer@An den Datentypen des Parameters hängen Sie also das Zeichen "@" an, das ja auch in Visual FoxPro selbst als Zeichen für die Übergabe als Referenz verwendet wird. Der Aufruf der Funktion muß dann wie folgt aussehen
APIFunktion( @nVar )Beim Aufruf wird das "@"-Zeichen also vorangestellt. Wenn eine Übergabe als Referenz erforderlich ist, wird in der Beschreibung der API-Funktion oft ein Datentyp verwendet, der mit "LP" beginnt, zum Beispiel entspricht LPDWORD in Visual FoxPro Integer@ und LPTSTR entspricht String@.
Mit dem soweit behandelten können Sie den DECLARE-Befehl in seinem vollen Funktionsumfang nutzen, aber es fehlt noch eine weitere Option, die in der Beschreibung als ParamName angegeben wird. Dieser Parameter wird von Visual FoxPro ignoriert und dient einzig und allein dazu, die DECLARE-Befehle für Sie als Programmierer verständlicher zu gestalten. Hier können Sie ähnlich dem PARAMETER-Befehl den einzelnen Parametern einer API-Funktion einen Namen zuordnen. Wenn Sie hier selbstsprechende Namen verwenden, wird die Definition auch für jemanden, der die API-Funktion nicht kennt, verständlicher. Weiter oben haben wir mit DECLARE die Funktion FindWindow() deklariert. Diese Deklaration unter Zuhilfenahme aller Optionen sieht wie folgt aus:
Declare Integer FindWindow in Win32API ;Sie müssen eine API-Funktion nur einmal deklarieren, und können sie dann beliebig oft nutzen. Allerdings wird kein Fehler erzuegt, wenn Sie eine API-Funktion mehrfach deklarieren. Um alle deklarierten API-Funktionen zu sehen, können Sie den Befehl LIST STATUS verwenden. Es ist leider nicht möglich, eine einzelne API-Deklaration zu löschen. Um aber alle API-Deklarationen zu entfernen, können Sie den Befehl CLEAR DLLS verwenden.
Wenn Sie Windows installieren, können Sie das in jedem beliebigen Verzeichnis. Um den Namen dieses Verzeichnisses zu ermitteln, beispielsweise um eine Datei dahin zu kopieren oder um bestimmte Dateien zu überprüfen, gibt es mehrere API-Funktionen. Die folgende Funktion liefert das Windows-Verzeichnis zurück:
Procedure WinDir Local lcWinDir, lnSize Declare Integer GetWindowsDirectory in Win32API ; String @lpBuffer, ; Integer uSize lcWinDir = Replicate(Chr(0),255) lnSize = GetWindowsDirectory( @lcWinDir, Len(m.lcWinDir) ) Return Left( m.lcWinDir, m.lnSize )Die Funtkion GetWindowsDirectory() erwarten einen String als Referenz, der lang genug ist, um den Verzeichnisnamen aufzunehmen. Um einen solchen String zu erzeugen, verwende ich die Funktion Replicate(), Sie können aber auch Space() verwenden. Da Windows keine Möglichkeit hat, festzustellen, wie lang der String ist, den wir übergeben haben,.müssen wir diese Länge ebenfalls übergeben. Zurückgegeben wird die Länge des Verzeichnisnamens, so daß wir mit Left() diesen Teil ausschneiden können. Auf ähnliche Weise können Sie das Systemverzeichnis ermitteln:
Procedure SysDir Local lcSysDir, lnSize Declare Integer GetSystemDirectory in Win32API ; String @lpBuffer, ; Integer uSize lcSysDir = Replicate(Chr(0),255) lnSize = GetSystemDirectory( @lcSysDir, Len(m.lcSysDir) ) Return Left( m.lcSysDir, m.lnSize )Wenn Sie temporäre Dateien anlegen wollen, sollten Sie das im temporären Verzeichnis machen. Sie können diesen Verzeichnis ebenfalls über eine Funktion ermitteln:
Procedure TempDir Local lcTempDir, lnSize Declare Integer GetTempPath in Win32API ; Integer uSize, ; String @lpBuffer lcTempDir = Replicate(Chr(0),255) lnSize = GetTempPath( Len(m.lcTempDir), @lcTempDir ) Return Left( m.lcTempDir, m.lnSize )Beachten Sie hier bitte zwei Dinge. Zum einen prüft diese Funktion nicht, ob das Verzeichnis wirklich existiert, und zum zweiten ist die Reihenfolge der Parameter anders als bei GetWindowsDirectory() und GetSystemDirectory().
Viele Applikationen bieten zwar nützliche Funktionen an, haben aber keine Automations oder DDE-interface und lassen sich daher nicht von einer Applikation direkt kontrollieren. Mit der Windows API ist es aber dennoch möglich, solche Applikationen zu kontrollieren, indem Benutzereingabe simuliert werden. Interessant ist beispielsweise die Fernsteuerung von WordPad, der in Windows enthaltenen Minimal-Textverarbeitung. Dieses Programm kann WindWord-Dokumente öffnen und drucken, eine Funktionalität, die von Programmierer immer wieder gewünscht wird. Die Antwort von Microsoft ist dann meist, daß man doch bitte WinWord über Automation nutzen möge. In der Praxis scheitert dies meist daran, daß der Kunde nicht nur zum Drucken von WinWord-Dokumenten für jeden Arbeitsplatz WinWord kaufen möchte.
Im folgenden wird die Fernsteuerung einer Anwendung am Beispiel des Druckens eines Dokumentes über WordPad demonstriert. Die hier verwendete Version ist die englische WordPad-Funktionen, so daß Sie für die deutsche Version einige Fenstertitel und die benötigten Tastendrücke anpassen müssen. Als erstes sind einige Konstanten von Nöten:
#DEFINE WM_CLOSE 16 #DEFINE WM_SYSCHAR 262 #DEFINE WM_CHAR 258 #DEFINE WM_COMMAND 273 #DEFINE SW_SHOW 5 #DEFINE ALT_KEY BitSet(0,29) #DEFINE IDOK 1Eine umfangreiche Liste aller Konstanten finden Sie in der Datei WIN32API.H auf der Konferenz-CD. Um mit WordPad eine Datei zu drucken, müssen wir als erstes das Programm starten:
Local lnHWNDVfp, lnHINSTANCE Declare Integer ShellExecute in Shell32.Dll ; Long Hwnd, ; String lpOperation, ; String lpFile, ; String lpParameters, ; String lpDirectory, ; Integer nShowCmd Set Library to (Home()+"FoxTools.Fll") Additive lnHWNDVfp = _WhToHwnd(_WMainWind()) lnHINSTANCE = ShellExecute( ; m.lnHWNDVfp, ; "open", ; "Write.Exe", ; "Demo.Doc", ; Sys(5)+CurDir(), ; SW_SHOW ; ) If lnHINSTANCE <= 32 Return EndifSelbstverständlich ist es auch möglich, den RUN-Befehl zu nutzen. Allerdings öffnet der RUN-Befehl ein DOS-Fenster, was unprofessionell aussieht, und zumdem ist die oben verwendete Funktion ShellExecute() sehr viel mächtiger. Im Gegensatz zu den bisher betrachteten Funktionen ist ShellExecute() kein Bestandteil der Windows API, die sie mit WIN32API deklarieren können, sondern befindet sich in der Shell32.Dll, die mit Windows ausgeliefert wird.
ShellExecute() kann Programme ausführen, aber auch Dateinnamen entgegennehmen und diese automatisch mit der zugeordneten Anwendung laden. Wenn Sie also den Namen einer DOC-Datei übergeben, wird automatisch WinWord mit der gewünschten Datei gestartet. Geben Sie eine HTML-Adresse an, so wird der Standardexplorer geöffnet und die gewünschte Seite angesprungen. Dabei wird, wenn möglich und notwendig, zugleich eine Internetverbindung aufgebaut. Im Prinzip entspricht ShellExecute() dem, was Sie aus dem Windows-Explorer machen können, wenn Sie eine Datei doppelklicken, bzw. bietet die Möglichkeiten, die Sie im Rechtklick-Menü zur Verfügung haben.
Um WordPad Nachrichten zukommen zu lassen, benötigen wir den Fenster Handle (HWND) von WordPad. Um diesen zu erhalten, gibt es mehrere Möglichkeiten. In unserem Fall wissen wir, wie das Fenster heißt und können direkt nach dem Fenstertitel suchen:
Local lnHWND, lnStart Declare Integer FindWindow in Win32API ; String lcClass, ; String lcTitle lnStart = Seconds() lnHWND = 0 Do While lnHWND == 0 and lnStart+5 > Seconds() lnHWND = FindWindow( NULL, "demo.doc - WordPad" ) Enddo If m.lnHWND == 0 ReturnEndif
Bei der Suche warten wir maximal 5 Sekunden, falls das Laden von WordPad etwas dauern sollte. Findet das Programm innerhalb dieses Zeitraumes den Fenstertitel nicht, brechen wir das Programm ab. Wenn Sie FindWindow() nutzen wollen, müssen Sie den exakten Fenstertitel wissen, inklusive Groß- und Kleinschreibung. Als nächstes simulieren wir einen Anwender, der im File-Menü die Funktion Print wählt, also erst ALT+F und dann P drückt.
Declare Integer PostMessage in Win32API ; Long nHWND, ; Long nMessage, ; Long wParam, ; Long lParam PostMessage( m.lnHWND, WM_SYSCHAR, Asc("f"), ALT_KEY ) PostMessage( m.lnHWND, WM_CHAR, Asc("p"), 0 )Mit PostMessage() können Sie an eine Applikation Nachrichten senden. Davon gibt es sprichwörtlich hunderte, die wichtigsten fangen alle mit WM_ an. Mit der Nachricht WM_SYSCHAR teilen Sie der Applikation mit, daß der Anwender eine Systemtaste gedrückt hat. Dies sind die Tasten mit der Kombination ALT. Zusätzlich übergeben Sie den ASCII-Code dieses Zeichen und eine Flag, bei dem das Bit 29 gestetzt ist. Im obigen Beispiel über ALT_KEY geregelt. Für normale Tastendrücke benutzen Sie WM_CHAR, dem Sie ebenfalls den ASCII-Code übergeben.
Nachdem also die Drucken-Funktion aufgerufen wurde, wird WordPad nun einen Dialog darstellen, in dem der Anwender die notwendigen Druckereinstellungen vornehmen könnte. Da wir dies automatisieren wollen, müssen wir erneut den den Fenster Handle für diesen Dialog finden und einen Anwender simulieren, der OK drückt:
Local lnHWNDPrinter, lnStart lnStart = Seconds() lnHWNDPrinter = 0 Do While lnHWNDPrinter == 0 and lnStart+1 > Seconds() lnHWNDPrinter = FindWindow( NULL, "Print" ) Enddo If m.lnHWNDPrinter == 0 Return Endif PostMessage( m.lnHWNDPrinter, WM_COMMAND, IDOK, 0 )In der englischen Version hat dieses Fenster den Titel "Print", weswegen wir danach suchen. Hier geben wir WordPad nur eine Sekunde Zeit, schießlich sollte das Darstellen des Print-Dialoges nicht soviel Zeit in Anspruch nehmen. Diesmal senden wir nicht eine WM_CHAR Nachricht, sondern WM_COMMAND. Diese besagt, daß ein Commandbutton gedrückt wurde. Als Parameter wird angegeben, um welche Befehlsschaltfläche es sich handelt. IDOK ist der Standardwert für den OK-Button. Nun braucht das Programm einige Zeit zum Drucken:
Inkey(1)Wenn Sie auf Nummer sicher gehen wollen, können Sie natürlich auch nach dem Fenster suchen, das den Druckfortschritt anzeigt und solange warten, bis dieses Fenster wieder verschwunden ist. Zu guter letzt teilen wir WordPad noch mit, das seine Dienste nicht länger benötigt werden und fordern es auf, sich zu beenden. Dazu senden wir die WM_CLOSE Nachricht an das Programm. Dies funktioniert übrigens auch mit anderen Programmen, solange diese noch nicht abgestürzt sind:
PostMessage( m.lnHWND, WM_CLOSE, 0, 0 )Die meisten API-Funktionen, insbesondere die interessanteren, verwenden Strukturen. Wenn Sie dazu etwas in der Visual FoxPro-Hilfe oder in der KnowledgeBase nachlesen wollen, finden Sie vermutlich nur recht einfache Beispiele, deren Code kaum noch verständlich ist. Abgeschlossen werden diese mit der Bemerkung, daß Sie komplexe Strukturen in Visual FoxPro nicht verwenden können. Wie Sie aber wissen, ist in Visual FoxPro nichts unmöglich, bis jemand den Beweis dafür erbracht hat, so auch bei Strukturen. Auf der Konferenz-CD finden Sie eine Klassenbibliothek STRUCT.VCX, mit der nicht nur die einfachen Beispiele sehr viel verständlicher werden. Diese Klassenbibliothek bietet alles, was Sie für die Verwendung von Strukturen benötigen, eingeschlossen Strukturen, die Zeiger auf Strings verwenden, einer kompletten Arrayverwaltung und der Möglichkeit Strukturen innerhalb von Strukturen abzubilden.
Was aber sind Strukturen, und warum soll es nicht möglich sein, diese unter Visual FoxPro zu verwenden? Strukturen lassen sich mit Objekten vergleichen, die keine Methoden haben. Sie bestehen aus einer Ansammlung von Daten, bei der jedes Element über seinen Namen angesprochen werden kann. Als einfaches Beispiel möchte ich einmal die POINT-Struktur anführen. Eine POINT-Struktur enthält die Informationen zu einer Bildschirmposition, also die Informationen zur X- und Y-Position. In C sieht die Definition dieser Struktur wie folgt aus:
typedef struct tagPOINT { // pt LONG x; LONG y; } POINT;
Diese Struktur besteht also aus zwei Integer-Werten, die Sie ja bereits bei der Beschreibung des DECLARE-Befehls kennengelernt haben. In C können Sie nun mit der folgenden Zeile eine Variable erzeugen, die beide Werte enthält:
POINT Punkt;
Auf die in dieser Struktur enthaltenen Werte können Sie nun wie in Visual FoxPro auf Objekte zugreifen:
Punkt.x = 514;
Nun nehmen wir einmal an, Sie finden in der Beschreibung einer Funktion die folgende Funktionsbeschreibung:
void myFunction( POINT *pt );
Diese Funktionsdeklaration besagt, daß die Funktion myFunction() keinen Parameter zurückgibt (void) und einen Parameter erwartet, der den Datentyp POINT hat, also eine Struktur ist, die die X- und die Y-Position enthält. Das Zeichen "*" besagt, daß Sie einen Zeiger übergeben sollen. Das entspricht der Übergabe als Referenz in Visual FoxPro. Wie können Sie nun diese Funktion mit DECLARE definieren? Nun, direkt geht das nicht.
Die Lösung liegt darin, wie Strukturen von C verwaltet werden. Für den C-Compiler sind Strukturen zusammenhängende Speicherbereiche. Jedes Element hat eine Position innerhalb dieses Speicherbereiches. Im obigen Beispiel etwa liegt das Element mit dem Namen x and Position 0, das Element y liegt an Position 4. Wenn Sie eine Struktur übergeben wollen, wird einfach dieser Speicherbereich übergeben. Wenn Sie die Struktur als Referenz übergeben wollen, wird die Adresse dieses Speicherbereiches übergeben.
Nun sind Strings in Visual FoxPro ebenfalls zusammenhängender Speicherbereiche. Wenn Sie einen String an eine API-Funktion übergeben, wird immer die Adresse dieses Speicherbereiches übergeben. Der Schluß liegt nahe, daß Sie irgendwie Strukturen als Strings übergeben können. Deklarieren Sie also die obigen Funktion wie folgt:
Declare MyFunction in Win32API String pt
Was aber muß dieser String nun enthalten, den Sie der Funktion übergeben? In der Tat ist es nicht ganz einfach, denn übergeben müssen Sie das Speichermuster, das auch der C-Compiler übergeben würde. Sie müssen die Werte für X und Y in ihre binäre Äquivalente konvertieren. Dazu müssen Sie wissen, wie Zahlen intern im Computer gespeichert werden, wie Sie eine Integer in vier Byts konvertieren und vieles mehr. In der Solution.APP, die mit Visual FoxPro geliefert wird, finden Sie dazu komplexe Ausdrücke bestehend aus CHR(), INT() und anderen Funktionen. Im obigen Beispiel würde etwa der String wie folgt erstellt werden, wenn Sie die Position X=514, Y=387 übergeben wollen:
lcPoint = ; CHR(0x02)+CHR(0x02)+CHR(0x00)+CHR(0x00)+; CHR(0x0B)+CHR(0x15)+CHR(0x00)+CHR(0x00)
Das ist zugegebenermaßen nicht ganz einfach. Glücklicherweise reicht es für Sie, wenn ich mir darüber den Kopf zerbreche, denn in der bereits erwähnten Klassenbibliothek wird diese Umrechnung für Sie vorgenommen. Halten wir also fest: Wenn Sie eine Struktur übergeben wollen, übergeben Sie eine Zeichenkette, die zuvor auf mysteriöse Weise erstellt werden muß. Betrachtet wir deswegen einmal, wie das obige Beispiel mit Hilfe der Struct-Komponente umgesetzt würde. Auf der linken Seite sehen Sie den C-Code, auf der rechten den Code in VFP. Da Sie bei der API-Programmierung immer wieder mit C konfrontiert werden, können Sie an Hand der Tabelle sehr gut sehen, wie ähnlich sich beide Codebeispiele sind:
C-Code | Visual FoxPro Code |
---|---|
Deklaration der Struktur |
|
typedef struct tagPOINT { // pt LONG x; LONG y; } POINT; |
Define Class POINT as Struct x = 0 y = 0 cMembers = ; "LONG l:x,"+; "LONG l:y," Enddefine |
Eine Variable diesen Types erzeugen |
|
POINT Punkt; |
loPunkt = NewObject( "POINT" ) |
Den Elementen der Struktur Wert zuweisen |
|
Punkt.x = 514; Punkt.y = 317; |
loPunkt.x = 514 loPunkt.y = 317 |
Die API-Funktion deklarieren |
|
void MyFunction( POINT *pt ); |
Declare MyFunction in Win32API ; String pt |
Die API-Funktion aufrufen |
|
MyFunction( &Punkt ); |
MyFunction( loPunkt.GetString() ) |
Bevor wir nun an Hand von einigen Beispielen sehen wollen, wie die Verwendung von Strukturen bei echten Windows-API Funktionen aussieht und dies an Hand einiger Beispiele betrachten, zurvor einige allgemeine Informationen zur Verwendung der Strukturkomponente.
Die von mir und Mark Wilden entwickelte Klasse Struct liegt in einer VCX-Bibliothek vor. Sie können diese Klasse also sowohl in Code als auch visuell ableiten und verwenden. Im folgenden beschränken wir uns auf die programmatische Deifnition, gleiches gilt aber auch für die Verwendung mit dem Klassendesigner. Bis Ende 1998 wird im CompuServe Forum der deutschsprachigen FoxPro User Group (GO DFPUG) eine detaillierte Anleitung für diese Komponente verfügbar sein. Struct.Vcx ist Public Domain und darf in Ihren Applikationen frei verwendet werden. Ebenso ist die Weitergabe zulässig und ausdrücklich erwünscht.
Bevor Sie eine Struktur in Visual FoxPro verwenden, müssen Sie immer eine Ableitung für diese Struktur erstellen, die auf der Klasse Struct basiert. Fügen Sie alle Elemente der Struktur als Eigenschaften dieser Klasse hinzu. Bei obigen Beispiel geschieht dies dadurch, daß die betreffenden Eigenschaften in das Define Enddefine aufgenommen und mit Standardwerten vorbelegt werden. Im Klassendesigner wählen Sie dazu Klasse|Neue Eigenschaft.
Nachdem Sie die Eigenschaften angelegt haben, müssen Sie ähnlich wie beim Befehl Declare den Datentyp der einzelnen Elemente definieren. Dazu dient die Eigenschaft cMembers, in der Sie in einem speziellen Format alle Eigenschaften deklarieren können. Es enthält eine kommaseparierte Liste aller Eigenschaften, in unserem Beispiel "LONG l:x, LONG l:y". Das Format eines solchen Eintrages ist wie folgt definiert:
[text] type:propertyIm Beispiel entspricht text "LONG", type "l" und property "x" bzw. "y". Der vorangestellte Text wird grundsätzlich ignoriert und ist optional. Es handelt sich wie bei den optionalen Parameternamen im Declare-Befehl lediglich um ergänzende Informationen für den Entwickler. Typischerweise wird hier der C-Typ hinterlegt, damit jeder die Möglichkeit hat, diese Informationen zu überprüfen. Zudem ist es einfacher "LONG" zu lesen und zu wissen, daß damit eine Integer-Zahl gemeint ist, als den eher kryptischen Bestandteil type zu analysieren.
Die Typdefinition ist recht umfangreich, damit Sie nicht allzuschnell an die Grenzen stoßen, aber mit System, so daß sie nach kurzer Eingewöhnungszeit problemlos verwendbar sind. Im Allgemeinen unterscheiden wir zwischen drei Grundtypen: numerischen Elementen, Zeichenketten und Strukturen. Für numerische Elemente ist Type wie folgt definiert:
[p] [s|u] l|w|b|dWie auch in der Visual FoxPro-Hilfe steht "[]" für optionale Parameter und "|" für eine Auswahl aus mehreren Möglichkeiten. Beispielsweise sind folgende Typen möglich: "l", "pul" oder "sw". Jeder Buchstabe hat dabei eine Funktion, die im folgenden beschrieben wird:
p: Es handelt sich um einen Zeiger auf ein Element. In C wird dies typischerweise durch ein "*" vor dem Namen der Eigenschaft oder durch ein "LP" im Datentyp ausgedrückt. So entspricht beispielswweise "int *prop" dem Typ "pi:prop" und "LPUINT prop" dem Typ "pul:prop". Wenn immer eine Eigenschaft ein Zeiger (engl. pointer) ist, stellen Sie der Typdefinition ein "p" voran. Beim Declare-Befehl haben Sie in diesem Fall meist das Zeichen "@" verwendet.
s: Es handelt sich um einen sogenannten vorzeichenbehafteten Wert (engl. signed), was gleichzeitig auch die Standardeinstellung ist. In C kann jeder Datentyp vorzeichenbehaftet sein, oder nicht. Wenn Sie beispielsweise den Datentyp "signed short int" sehen, bedeutet dies, daß die Variable Werte zwischen -32768 und 32767 annehmen kann. Handelt es sich dagegen um ein "unsigned short int", liegt der Wertebereich zwischen 0 und 65535. Eine vorzeichenbehaftete Variable kann also negative Werte annehmen, allerdings ist dann der größt mögliche Wert niedriger, als wenn keine negativen Werte angenommen werden können. Typischerweise können Sie diesen Schalter ignorieren, wichtig ist nur sein Pendant
u: Hierbei handelt es sich um einen nicht vorzeichenbehafteten Wert. Solche Werte werden in Windows relativ selten eingesetzt. Sie erkennen diese daran, daß entweder das Wort "unsigned" im Datentyp auftaucht, wie bei "unsigned int", oder aber der Name des Datentyps mit "U" beginnt, wie in "UINT".
l: (Achtung, dies ist ein kleines "L"). Es handelt sich um einen Integerwert, der insgesamt 32 Bit Länge hat. Dies entspricht in Declare sowohl "Long" als auch "Integer" direkt. Die meisten Datentypen von Window sind Integer.
w: Es handelt sich um einen 16-Bit Wert (WORD), der entweder Werte zwischen 0 und 65535 oder zwischen -32768 und 32767 annehmen kann. Dies entspricht im Declare-Befehl dem Datentyp "Short".
b: Es handelt sich um einen Byte-Wert, die in Windows relativ selten verwendet werden. Ein Byte kann entweder einen Wert zwischen 0 und 255 oder zwischen -128 und 127 annehmen, je nachdem ob es zusammen mit "u" oder "s" verwendet wird.
Für Strings gibt es in der API-Programmierung zwei Ausprägungen. Die von Visual FoxPro unterstützten Strings sind die sogenannten null-terminierten Strings. Das heißt, sie haben keine vorgegebene Länge, das Ende eines Strings erkennt ein Programm am abschließenden CHR(0). Daher auch der Name. In C werden diese Strings wie folgt definiert:
char *String;Die andere Art von Strings hat eine fest definierte Länge und benötigt daher kein Endekennzeichen. In C-Definitionen finden Sie dafür oft folgende Schreibweisen:
char String[20];Natürlich sind in der Praxis nicht alle Zeichenketten immer gleich lang, weswegen für letzere Art von Zeichenketten oft ein Mischansatz verwendet wird. Wenn ein String beispielsweise nur aus 12 Zeichen besteht, werden die übrigen 8 Buchstaben auf CHR(0) gesetzt. Der String wird mit also CHR(0) aufgefüllt. Daraus ergibt sich für die Struct-Komponente folgende Typdefinition für Zeichenketten:
[p] [x] ([0]c)|z [length]p: Wie auch bei den numerischen Werten gibt "p" an, daß es sich um einen Zeiger handelt. In Strukturen muß jedes Element an einer eindeutigen Position stehen. Nullterminierte Strings haben aber keine feste Länge, so daß nachfolgende Elemente an verschiedenen Positionen stehen würden. Daher werden nullterminierte Strings im Regelfall als Zeiger in eine Struktur aufgenommen, wie Sie im obigen Beispiel an den typischen Kennzeichen für Zeiger erkennen können, dem "*" und der Kombination "LP". Zeichenketten mit einer festen Länge dagegen werden üblicherweise direkt in die Struktur eingebunden, sind also keine Zeiger.
x: Windows 9x und Windows NT verwenden unterschiedliche Zeichentabellen. Während Windows 9x den sogenannten ANSI Code verwendet, bei der jedes Zeichen genau ein Byte groß ist, verwendet WindowsNT in vielen Fällen Unicode, bei der jedes Zeichen zwei Byte groß ist. Wenn Sie ein Programm schreiben, daß auf beiden Plattformen laufen soll, müssen Sie je nach Betriebssystem eine von beiden Zeichentabellen verwenden. Um Ihnen diese Arbeit abzunehmen, können Sie den Code "x" verwenden. Wenn dieser Code in der Typdefinition auftaucht, wird unter Windows 9x immer ein Byte pro Zeichen verwendet, unter Windows NT dagegen zwei Byte. Dies entspricht dem Windows Datentypen TCHAR.
0: (Ziffer null!). Wie bereits beschrieben werden Zeichenketten mit einer fixen Länge üblicherweise mit CHR(0) aufgefüllt. Genau das macht die Struct-Komponente ebenfalls automatisch. Wenn eine API-Funktion einen Wert zurückliefert, wollen Sie unter Umständen aber gar nicht diese zusätzlichen CHR(0)-Werte haben, sondern nur den String, wie er auch zurückgeliefert wurde. Wenn Sie diesen Code angeben, werden automatisch alle im zurückgegebenen String enthaltenen CHR(0)-Werte entfernt.
c: Es handelt sich um einen String, der nicht automatisch mit CHR(0) abgeschlossen wird. Typischerweise hat er eine feste Länge. Wenn Sie keine Länge angeben, wird die aktuelle Länge verwendet.
z: Es handelt sich um einen String, der automatisch mit CHR(0) terminiert wird. Wenn Sie einen solchen String zurückerhalten, wird das CHR(0) am Ende automatisch abgeschnitten.
length: Gibt die Länge des Stringes in Zeichen an. Wenn eine Länge erfordert ist, und Sie keine angeben, wird immer die aktuelle Länge des Strings verwendet. Sollte ein String kürzer sein, als durch diesen Wert angegeben, so wird er mit CHR(0) aufgefüllt.
In vielen Strukturen von Windows sind innerhalb der Struktur weiter Strukturen enthalten. Mit der Struct-Komponente können Sie solche Strukturen sehr einfach verwalten. Die innere Struktur legen Sie als zweites Objekt an. In der äußeren Struktur definieren Sie eine Eigenschaft, die eine Referenz auf die enthaltene Struktur enthält. Um eine Eigenschaft als Struktur zu kennzeichnen, verwenden Sie folgende Notation:
[p] oMit o geben Sie an, daß es sich um eine zweite Struktur handelt. Wenn diese Struktur nicht direkt eingebunden werden soll, sondern nur ein Zeiger auf dieser Struktur, so verwenden Sie zusätzlich den Code p. Nehmen wir als Beispiel einmal eine Struktur mit dem Namen LINE an, die eine Linie beschreiben soll. Eine Linie können Sie unter anderem durch ihren Anfang- und ihren Endpunkt definieren. Die Definition für einen Punkt (POINT) haben wir ja bereits weiter oben betrachtet.
Define Class LINE as struct Start = NULL Ende = NULL cMembers = ; "POINT o:Start,"+; "POINT o:Ende" EnddefineNun können Sie wie folgt eine Linie zeichnen, wenn die Funktion zum Zeichnen einer Linie einen Zeiger auf eine LINE Struktur erwarten würde:
loLine = NewObject( "LINE" )Der erste Schritt ist also wie oben gezeigt immer die Erstellung einer Klasse, die die entsprechende Struktur darstellt. Die Verwendung dieser Struktur gliedert sich dann in mehrere Schritte: Als erstes erzeugen Sie eine Instanz dieser Struktur mit NewObject(). Anschließend speichern Sie die gewünschten Werte in den Eigenschaften des Struktur-Objektes und rufen schließlich die Methode GetString() auf, die Ihnen einen String liefert, den Sie als Struktur an die API-Funktion übergeben können.
Einige API-Funktionen ändern Werte in der übergebenen Struktur, zum Beispiel wenn Sie bestimmte Informationen abrufen. In diesem Fall rufen Sie anschließend die Methode SetString() auf, die einen String entgegennimmt und die darin gespeicherten Werte wieder in das Objekt schreibt. Das folgende Beispiel ermittelt die aktuelle Systemzeit:
Local loTime, lcString Declare GetLocalTime in Win32Api String @cTime loTime = NewObject( "SYSTEMTIME" ) lcString = loTime.GetString() GetLocalTime(@lcString) loTime.SetString(lcString) ? PadL(loTime.nHour,2,"0")+":"+ ; PadL(loTime.nMinute,2,"0")+":"+ ; PadL(loTime.nSecond,2,"0") Define Class SYSTEMTIME as Struct nYear = 1 nMonth = 2 nDayOfweek = 3 nDay = 4 nHour = 5 nMinute = 6 nSecond = 7 nMilliSecond = 8 cMembers = ; "w:nYear, w:nMonth, w:nDayOfWeek,"+; "w:nDay, w:nHour, w:nMinute, w:nSecond,"+; "w:nMilliSecond" Enddefine=0
SYSTEMTIME ist eine von Windows definierte Struktur, die in Visual FoxPro einer DateTime-Variable entsprechen würde. Als erstes wird diese Struktur erstellt und in einem String konvertiert. Die API-Funktion GetLocalTime, die die aktuelle Systemzeit ermittelt, erhält diese Struktur als Referenz übergeben und speichert die aktuellen Werte darin. SetString() schreibt diese Werte in die Eigenschaften aus denen Sie die aktuelle Zeit problemlos auslesen können.
Nun, diese API-Funktion ist durch die Visual FoxPro Funktion DateTime() natürlich von geringeren Interesse für die meisten Entwickler. Interessant ist allerdings, daß mit der korrespondierenden Funktion SetLocalTime() die Zeit gesetzt werden kann. Noch interessante aber dürfte es sein, Informationen über das Betriebssystem zu erhalten. Bekanntermaßen ist die Funktion OS() nicht sehr genau, für viele Programme aber ist es relevant, ob das Programm unter Windows 95, Windows 98, Windows NT 3.51, Windows NT 4.0 oder Windows NT 5.0 läuft, insbesondere dann, wenn Probleme auftauchen oder versionsabhängige Funktionen genutzt werden sollen. Dafür ist die Funktion GetVersionEx() gedacht, die detailliert die verwendete Plattform (Windows 9x, Windows NT, Windows32S) und die Version zurückgibt. Das folgende Beispiel zeigt diese Informationen an:
Declare Integer GetVersionEx in Win32API String @cVersion loVersion = NewObject( "OSVERSIONINFO" ) loVersion.dwOSVersionInfoSize = loVersion.SizeOf() lcVersion = loVersion.GetString() GetVersionEx( @lcVersion ) loVersion.SetString( m.lcVersion ) ? "SizeOf :", loVersion.dwOSVersionInfoSize ? "Major version:", loVersion.dwMajorVersion ? "Minor version:", loVersion.dwMinorVersion ? "Build number :", Transform(loVersion.dwBuildNumber,"@0") ? "Platform ID :", loVersion.dwPlatformID ? "Version :", loVersion.szCSDVersion Define Class OSVERSIONINFO as Struct dwOSVersionInfoSize = 0 dwMajorVersion = 0 dwMinorVersion = 0 dwBuildNumber = 0 dwPlatformID = 0 szCSDVersion = "" cMembers = ; "DWORD l:dwOSVersionInfoSize,"+; "DWORD l:dwMajorversion,"+; "DWORD l:dwMinorVersion,"+; "DWORD l:dwBuildNumber,"+; "DWORD l:dwPlatformID,"+; "TCHAR xz128:szCSDVersion" EnddefineViele Strukturen haben als erstes ein Element, daß die Größe der Struktur angibt. Unter C gibt es keine Möglichkeit, festzustellen, wie groß eine übergebene Struktur tatsächlich ist. Sollte sich in späteren Versionen die Größe einmal ändern, da neue Elemente hinzukommen, so muß die Funktion das merken können, andernfalls würde es auf den alten Systemen zu einem Absturz kommen. Um die Größe einer Struktur zu bestimmen, können Sie die Methode SizeOf() aufrufen. sizeof() ist eine C Funktion, die Sie in C-Quellcode häufiger finden können. Um die Umsetzung möglichst einfach zu machen, wurde dieser Name beibehalten.
Ein weiterer häufig geäußerter Wunsch ist das Drucken eines Textes im RTF-ActiveX Control. Die Hilfe bietet Ihnen dafür die SelPrint() Methode an und meint lapidar, übergeben Sie den DeviceContext für den gewünschten Drucker. Ganz so einfach ist es natürlich nicht, aber mit der Struct-Komponente ist es dennoch möglich:
LParameter toRTF, tcPrinter, tcDoc Declare Integer StartDoc in Win32Api ; Integer, String Declare Integer StartPage in Win32Api integer Declare integer EndPage in Win32Api integer Declare integer EndDoc in Win32Api integer Declare Integer CreateDC in Win32Api ; String, String, String, String Declare Integer DeleteDC in Win32Api Integer Local lnHDC, loDocInfo lnHDC = CreateDC( "WINSPOOL", m.tcPrinter, NULL, NULL ) loDocInfo = NewObject( "DOCINFO" ) loDocInfo.cbSize = loDocInfo.SizeOf() loDocInfo.lpszDocName = m.tcDoc StartDoc( m.lnHDC, loDocInfo.GetString() ) StartPage( m.lnHDC ) toRTF.SelPrint( m.lnHDC ) EndPage( m.lnHDC ) EndDoc( m.lnHDC ) DeleteDC( m.lnHDC ) Return Define Class DOCINFO as Struct cbSize = 0 lpszDocName = NULL lpszOutput = NULL lpszDataType = NULL fwType = 0 cMembers = ; "int l:cbSize,"+; "LPCTSTR pz:lpszDocName,"+; "LPCTSTR pz:lpszOutput,"+; "LPCTSTR pz:lpszDatatype,"+; "DWORD l:fwType" EnddefineAls erstes erzeugen wir den vom RTF-Control geforderten DeviceContext. Dies erledigt die Funktion CreateDC(), der wir den Namen des zuständigen Treibers, hier WINSPOOL, und den Namen des des Gerätes, hier der Druckername, wie er von GetPrinter() zurückgegeben wird, übergeben. Der dritte Parameter muß immer NULL sein, der vierte Parameter könnte einen Zeiger auf eine DEVMODE-Struktur entgegennehmen. Diese müßten wir uns vom Druckertreiber geben lassen und könnten dann Einstellungen wie die Seitenorientierung, etc. ändern. Wenn dieser Parameter wie im Beispiel oben NULL ist, dann werden die aktuellen Standardeinstellungen für den Drucker verwendet.
Nachdem ein DeviceContext ermittelt wurde, erzeugen wir eine DOCINFO-Struktur. Diese enthält Angaben für den Druckertreiber, die den Druckauftrag betreffen. Das Element lpszDocName enthält den Namen, der im Druckerspooler während des Druckens angezeigt wird. Bevor wir drucken können, müssen wir dem Druckertreiber noch mitteilen, daß wir für ihn einen neuen Druckauftrag haben. Dies erledigt die Funktion StartDoc(). Sie erzeugt auch den Eintrag im Druckerspooler, so daß der Anwender sehen kann, daß sein Text gedruckt wird. Mit StartPage() wird eine neue Seite erzeugt. Nun endlich wird die SelPrint() Methode des RTF-Controls aufgerufen, daß den Inhalt an der aktuellen Position druckt. Zum Abschluß teilen wir dem Druckertreiber noch mit, daß die aktuelle Seite nun abgeschlossen wurde (EndPage) und der Druckauftrag erfolgreich beendet wurde (EndDoc). Da wir nun den DeviceContext nicht mehr benötigen, wird dieser ebenfalls mit DeleteDC() freigegeben.
Diese drei Beispiele vermitteln nur einen ersten Eindruck dessen, was mit der Struct-Komponente möglich ist. Viele API-Funktionen erfordern umfangreichere Programmierungen und den Aufruf mehrerer API.-Funktionen. So können Sie beispielsweise über zahlreiche Funktionen alle aktuellen Durckaufträge eines Druckers erfahren. Dann einzelnen Druckaufträge unterbrechen, abrechen, der Drucker offline schalten und alles machen, was Sie auch interaktiv im Druckermanager machen können. Sie können über die Netzwerkfunktionen Informationen über den Server, die Benutzer und Benutzergruppen, die verfügbaren Resourcen, usw erfahren. Über die RAS-Funktionen können Sie das DFÜ-Netzwerk ansteuern und so beispielsweise Internet-Verbindungen aufbauen.