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 will ich Ihnen eine Einleitung in die Verwendung von APIs aus Visual FoxPro heraus geben. Nicht Thema dieser Session ist die fortgeschritten API-Programmierung und die Erstellung eigener APIs.
API steht für Application Programming Interface. Im Prinzip handelt es sich um eine Sammlung von Funktionen, die eine Anwendung, in den meisten Fällen 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, .EXE oder .DRV Dateien gespeichert, die im Windows/System-Verzeichnis bzw. Windows/System32-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 Internet-Anbindung. 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.
In den Dateien, in denen eine API gespeichert ist, ist als einzige Angabe der Name der Funktion enthalten. Im Gegensatz zu ActiveX Controls und OLE Servern kann Visual FoxPro daher nicht selbständig ermitteln, welche Parameter an diese Funktion übergeben werden müssen. Daher müssen Sie, bevor Sie eine API-Funktion in Visual FoxPro verwenden, Visual FoxPro mitteilen, wie die Funktion heißt, wo sie sich befindet und welche Parameter ihr übergeben werden müssen. 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]
[cParamType1 [@] ParamName1,
cParamType2 [@] ParamName2, ...]
Zwei Parameter müssen immer angegeben werden: FunctionName und LibraryName. Ersteres 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 Kleinschreibung 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. Sollten Sie als Bibliothek WINAPI32 verwenden, erhalten Sie eine Fehlermeldung bereits bei der Deklaration, bei allen anderen Bibliotheken hingegen erst beim Aufruf. Insbesondere wenn Deklaration und Aufruf nicht nahe beieinander stehen, kann dies Anfangs leicht zu der Vermutung führen, der Aufruf sei falsch.
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, String
Kö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, String
Erlaubt 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 die 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. Eine Ausnahme gibt es aber dennoch: Einige API-Funktionen haben den gleichen Namen wie Visual FoxPro Funktionen. Hier müssen Sie einen Alias verwenden, da andernfalls die Visual FoxPro Funktion Vorrang hat.
Das Konzept der Datentypen ist meiner Erfahrung nach einer der wesentlichen Gründe, warum viele Visual FoxPro Entwickler denken, daß API Programmierung schwierig sei. Wie auch Visual FoxPro Funktionen erwarten API Funktionen bestimmte Datentypen, wenn Sie diese Funktionen aufrufen. In Visual FoxPro gibt es die folgenden Datentypen: Zeichen, Währung, Datum, Datum/Zeit und numerische Werte. Zusätzlich gibt es Feldtypen wie Memo, OLE Objekt, Double, Float und Integer, die beim Lesen in den Speicher in Zeichen oder numerische Werte konvertiert werden.
Das gleiche gilt für C, nur hat C andere Datentypen. Um sie verwenden zu können, müssen Sie wissen, wie C Datentypen nach Visual FoxPro übersetzt werden. Grundsätzlich gibt es die folgenden Datentypen:
Integers sind numerische Werte ohne Nachkommastellen, wie auch der Feldtyp Integer in Visual FoxPro. Man unterscheidet Integers auf Grund ihres Speicherbedarfes, der auch gleichzeitig den Wertebereich einer solchen Variablen definiert. 8-bit Integer werden byte oder char genannt. Ein 16-bit Wert ist ein word oder manchmal auch ein short int. 32-bit Werte werden integer, long oder dword genannt. Seltener tauchen 64-bit Werte auf, die longlong oder long64 genannt werden.
Zusätzlich können Integer in zwei Ausprägungen auftreten, als vorzeichenlose oder vorzeichenbehaftete Werte. Eine vorzeichenbehaftete Integer Variable kann negativ sein, gleichzeitig aber wird der Bereich der positiven Werte halbiert. So kann ein signed char Werte zwischen -128 und +127 annehmen, während ein unsigned char Werte zwischen 0 und 255 annehmen kann. In beiden Fällen sind dies 256 unterschiedliche Werte. Die meisten Parameter für API-Funktionen sind Integers, und dort wiederum sind die meisten 32-bit Werte.
String gibt es in C nicht. Auch wenn es sich seltsam anhört, so sind in C doch alle Zeichenketten eigentlich Arrays von einzelnen Buchstaben. Dieses Arrays, die ich im folgenden als String bezeichnen möchte, kommen in zwei Ausprägungen vor: Strings mit einer vordefinierten Länge und variable Strings. Strings mit einer vordefinierten Länge werden beispielsweise als char variable[32] definiert, was einen String erzeugt, der 32 Zeichen aufnehmen kann. Üblicherweise werden solche Strings nur in Strukturen benötigt. Bei variablen Strings gibt es kein Kennzeichen, wie etwa bei Delphi, da die Länge eines Strings bestimmt. Daher wurde in C als Standard definiert, daß das Zeichen CHR(0), oder '\0' in C, das Kennzeichen für das Ende eines Strings ist. Wenn ein C-Programm die Länge eines Strings bestimmen möchte, zählt es die einzelnen Zeichen durch, bis es einen CHR(0) Wert findet. Dessen Position minus 1 bestimmt dann die Länge.
Ein String besteht immer auch Buchstaben. Diese können in zwei verschiedenen Formaten auftreten, als ANSI und als UNICODE Zeichen. Ein ANSI Zeichen belegt ein Byte im Speicher und entspricht dem, was die CHR() Funktion zurückgibt. Ein UNICODE Zeichen benötigt zwei Bytes, wobei in westlichen Zeichensätzen das zweite Byte immer 0 ist. Ein Unicode String wird mit CHR(0)+CHR(0) beendet.
Floating-Point Werte sind das, was ein Visual FoxPro Entwickler als numerischen Wert bezeichnen würde. Eine solche Variable kann große Werte annehmen und auch Nachkommastellen enthalten. Diesen Datentyp gibt es in zwei Variationen. Float oder Single belegen 32 Bit Speicher (vier Byte), kann allerdings nur 6 Nachkommastellen genau abbilden. Ein Double benötigt 64 Bit und ist das gleiche, was Visual FoxPro als numerischen Wert verwendet. Beide Typen werden wirklich nur dann benötigt, wenn Nachkommastellen oder sehr große Zahlen erforderlich sind, beispielsweise bei Berechnungen.
Im Prinzip ist das alles. C bietet einige weitere Datentypen, wie zum Beispiel Aufzählung (enumeration), aber grundsätzlich decken diese drei Datentypen 99% aller verwendeten Typen ab. Das klingt zu einfach, oder? Wie werden Objekte verwaltet? Was ist mit einem Datum oder logischen Wert? Und wieso lassen sich in der API Dokumentation so viele seltsame Datentypen wie HWND; HINSTANCE, POINT, usw. finden?
Der Grund hierfür liegt in einer Eigenschaft von C, die strikte Typisierung genannt wird. Eine Ausprägung dieser Eigenschaft ist, daß eine Variable nur Werte akzeptiert, die ihrem Typ entsprechen. Sie können in C nicht einer Variablen erst ein Zeichen und dann einen numerischen Wert zuweisen. In Visual FoxPro wird dies üblicherweise über Namenskonventionen geregelt. So ist beispielsweise cName ein String, während nAnzahl ein numerischer Wert ist. Die strikte Typisierung führt zu einem weiteren Feature. Man kann einen neuen Datentyp definieren, indem man einem bestehenden Typ einen neuen Namen zuweist. Sie können das in Visual FoxPro mit dem Alias einer Tabelle vergleichen. Sie können die selbe Tabelle mit mehrfach mit einem anderen Alias öffnen. Jeder Alias hat seinen eigenen aktiven Index, seinen eigenen Filter, seinen eigenen Datensatzzeiger, usw, aber alle greifen letztendlich auf die selbe Tabelle zu. Dies ist vergleichbar mit dem Erzeugen neuer Datentypen. Jeder Typ scheint unterschiedlich zu sein, aber alle werden intern vollkommen identisch abgelegt.
Windows macht intensiv Gebrauch von dieser Möglichkeit. Zum Beispiel ist der Typ LONG eigentlich ein long signed int. Ein UINT ist ein long unsigned int. Beides sind 32-bit Integer Werte. Die Namen werden in zahlreichen Include Dateien definiert, die Sie in \VC98\Include Verzeichnis finden können, sofern Sie entweder VC++ oder Visual Studio haben. Und weil das so zu einfach wäre, können Sie einen selbst definierten Typ wiederum in der Definition eines anderen Typs verwenden.
Herauszufinden, welches der eigentliche Grunddatentyp ist, der in einer API-Funktion verwendet wird, ist in der Tat eine schwierigsten Aufgaben bei der Programmierung der API. Eine Merkregel ist, wenn Sie nicht double, char oder str in Namen finden, handelt es sich vermutlich um einen 32-bit Wert. Es dauert eine Weile, sich daran zu gewöhnen, aber anschließend werden Sie Datentypen ganz routiniert auflösen können. Die folgende Liste soll Ihnen den Anfang erleichtern:
Windows Datentyp | C Datentyp |
---|---|
BOOL |
32-bit signed integer. 0 bedeutet wahr, alles andere falsch. |
BOOLEAN |
siehe BOOL. |
BYTE |
8-bit integer |
CHAR |
8-bit integer. Sie können diese mit CHR() konvertieren. |
COLORREF |
32-bit unsigned integer. Der gleiche Wert, den RGB() liefert. |
DWORD |
32-bit unsigned integer |
FLOAT |
float |
HANDLE |
32-bit unsigned integer |
HBITMAP |
32-bit unsigned integer |
HBRUSH |
32-bit unsigned integer |
HDC |
32-bit unsigned integer |
HFONT |
32-bit unsigned integer |
HGDIOBJ |
32-bit unsigned integer |
HICON |
32-bit unsigned integer |
HINSTANCE |
32-bit unsigned integer |
HPEN |
32-bit unsigned integer |
HWND |
32-bit unsigned integer |
LONG |
32-bit signed integer |
SHORT |
16-bit signed integer |
TCHAR |
ANSI-Code für ANSI-Funktionen |
UCHAR |
ANSI Code Zeichen |
UINT |
32-bit unsigned integer |
ULONG |
32-bit unsigned integer |
USHORT |
16-bit unsigned integer |
WCHAR |
UNICODE Zeichen |
WORD |
16-bit unsigned integer |
In dieser Liste können Sie erkennen, daß logische Werte als numerische Werte behandelt werden. Per Definition bedeutet 0 .F. und alles andere .T. In Visual FoxPro können Sie daher folgendes verwenden, um einen numerischen Wert in einen logischen Wert zu konvertieren:
lErgebnis = ( nErgebnis # 0 )
Der DECLARE-Befehl unterstützt nicht alle der grundlegenden Datentypen, die ich eben aufgeführt habe, aber alle, die Sie für die meisten einfachen Funktionen benötigen. Im einzelnen kennt Visual FoxPro die folgenden Datentypen für API Aufrufe:
LONG und INTEGER sind vollkommen identisch und kennzeichnen beide vorzeichenbehaftete 32-bit Integer Werte. Dies ist der gebräuchlichste Datentyp in der API Programmierung. Obwohl dieser Typ vorzeichenbehaftet ist, können Sie auch Werte zuweisen, die jenseits der Grenze von 2^31 liegen. Visual FoxPro konvertiert diese automatisch.
SHORT ist ein 16-bit Wert, der allerdings nur von sehr wenigen API-Funktionen verwendet wird. SHORT kann in Visual FoxPro offiziell nur als Rückgabewert, nicht aber als Parameter verwendet werden. In Visual FoxPro 6.0 mit Service Pack 3 funktioniert dies zwar, allerdings haben alle vorherigen VFP Versionen Probleme, einen SHORT Wert per Referenz zu übergeben. Da es sich bei SHORT als Parameter auch nicht um eine dokumentierte Eigenschaft handelt, sollten Sie mit seiner Verwendung vorsichtig sein.
SINGLE ist eine 32-bit Fließkommazahl, die eigentlich kaum verwendet wird.
DOUBLE ist eine 64-bit Fließkommazahl, die immer dann verwendet wird, wenn Zahlen mit Nachkommastellen benötigt werden. Allerdings hat Visual FoxPro die unangenehme Eigenschaft, daß Fließkommazahlen, die per Referenz übergeben werden, ihre Nachkommastellen verlieren. Sie müssen daher nach dem Aufruf der API-Funktion immer erst 0.00000000000 zum Rückgabewert addieren, bevor Sie den Wert verwenden. Andernfalls rechnen Sie ohne Nachkommastellen weiter.
STRING entspricht mehr oder weniger einem ganz normalen Visual FoxPro String. Allerdings wird in der C Welt, wie bereits erwähnt, ein String durch CHR(0) beendet, weswegen Sie dieses Zeichen nicht verwenden sollten. Sie müssen das Zeichen nicht selbst anfügen, denn wenn Visual FoxPro einen String übergibt, so fügt es dies selbst an, und entfernt es auch wieder, wenn es das Ergebnis in eine Visual FoxPro Variable schreibt. Nur bei der Übergabe eines UNICODE Strings müssen Sie selbständig ein weiteres CHR(0) anfügen.
Einige Datentypen wurden nicht aufgeführt. Ein Datum, beispielsweise, 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. Dies ist aber nicht Bestandteil dieser Session.
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 Integer
Diese 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 ;
String tcWindowClass, ;
String tcWindowTitle
Sie müssen eine API-Funktion nur einmal deklarieren, und können sie dann beliebig oft nutzen. Allerdings wird kein Fehler erzeugt, 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 Funktion 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 1
Eine 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
Endif
Selbstverständlich ist es auch möglich, den RUN-Befehl zu nutzen. Allerdings öffnet der RUN-Befehl ein DOS-Fenster, was nicht professionell aussieht. Zudem 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 Dateinamen 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
Return
Endif
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 Kennzeichen, bei dem das Bit 29 gesetzt 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 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, schließlich sollte das Darstellen des Druck Dialoges nicht soviel Zeit in Anspruch nehmen. Diesmal senden wir nicht eine WM_CHAR Nachricht, sondern WM_COMMAND. Diese besagt, daß eine Befehlsschaltfläche 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 )