FoxPro

FoxPro Developer's Conference '94

Session 244
Einsatz des Library Contruction Kit

Kurt Zander
Papst GmbH


 

1. Vorbemerkungen

Wie Sie sicherlich wissen, ist FoxPro für Windows mehr als nur ein Manager für Datenbanken. Es ist eine komplette Entwicklungsumgebung - auf Windowsebene sogar eine sehr mächtige. Trotzdem kommt es immer wieder vor, daß auch ein gutes Produkt wie FoxPro Schwachstellen aufweist oder gerade die Funktionaliät nicht bietet, die momentan benötigt wird und auch in FoxPro-Code nicht oder nur sehr umständlich umgesetzt werden kann.

Desweiteren ist FoxPro keine echte Compilersprache wie z.B. C oder Clipper, aber auch keine reine Interpretersprache wie dBase. FoxPro steht irgendwo dazwischen, ist aber nicht gerade das schnellste Tool (natürlich außer Rushmore).

Für diese Fälle kommt die offene Architektur auch von FoxPro zum Tragen. Durch die API (Application Programming Interface) Möglichkeit steht Ihnen prinzipiell nichts mehr im Wege, Funktionen zu entwickeln, die FoxPro selbst nicht bietet oder die eine höhere Geschwindigkeit gewährt. Dies können Sie mit dem LCK, welches in der Professional Version FoxPro 2.6 Bestandteil des Paketes ist, verwirklichen.

Mit dem LCK erhalten Sie eine Schnittstelle zur C-Programmierung gewünschter neuer Routinen. Diese Routinen werden in einer Library, unter Windows mit der Endung FLL (Fox Link Library), eine spezielle Version der DLL's (Dynamic Link Library) zusammengefaßt und in Ihrer Applikation durch den Befehl SET LIBRARY TO <libname> [ADDITIVE] aktiviert. Sie können dann jede Funktion aus der Library aufrufen wie eine FoxPro-Funktion. Sie sind also den Beschränkungen einer Prozedurdatei (SET PROCEDURE TO <proc>) nicht mehr ausgesetzt.

Ein kleiner Wermutstropfen ist leider nicht zu verhindern. Um eigene Libraries zu erstellen, benötigen Sie nicht nur das LCK und Grundkenntnisse in C. Sie müssen auch einen C-Compiler haben, der in der Lage ist, Dynamic Link Libraries zu erstellen (wie z.B. der MS C7-Compiler oder auch Visual C++ von Microsoft). Wenn Sie zusätzlich noch die Möglichkeiten von Windows nutzen wollen, muß zusätzlich das Windows-SDK auf Ihrem Rechner installiert sein.

Wenn diese Voraussetzungen alle erfüllt sind, steht Ihnen nichts mehr im Wege, die erweiterten Möglichkeiten durch das LCK zu nutzen. Alleine das LCK stellt Ihnen über 130 Funktionen zusätzlich zur Verfügung.

Eine weitere scheinbare Einschränkung des API ist, daß die Schnittstelle ausschließlich auf C ausgerichtet ist. So werde ich immer wieder von Clipper-Programmierern gefragt, die endlich einsehen, auf der Windows-Ebene Applikationen erstellen zu müssen, was denn mit ihren Assembler-Routinen ist. Diese erhalten dann als Antwort - der MS-C Compiler bietet die Möglichkeit des Inline-Assembler, so daß auch solche Routinen, wenn es nun sein muß, adaptiert werden können.


 

2. Aufbau einer eigenen C-Library

Das Entwickeln von FLL's unter Zuhilfenahme des LCK ist nicht so schwierig, wie es das Handbuch auf dem ersten Blick erscheinen läßt. Eine Quelldatei läßt sich immer in 4 Blöcke aufteilen:

  1. der Setup-Bereich, in dem die #include Anweisungen stehen, globale Variablen initialisiert werden, eigene Definitionen (#define) formuliert werden usw.
  2. die Funktionen, die Sie entwickeln - sowohl interne Funktionen, als auch die, die über FoxPro angesprochen werden
  3. eine ArrayInfo-Struktur, die für jede für FoxPro sichtbare Funktion den Aufrufnamen, den internen Namen, die Anzahl der Parameter und die Parametertypen beinhaltet
  4. und eine Strukturdefinition, u.a. mit Pointern auf die Info-Struktur.

Konkret würde dies wie folgt aussehen:

	/* Bereich a) */
	#include <windows.h>	/* Headerfile zum SDK */
	#include <pro_ext.h>	/* Headerfile zum LCK */
	#include "my_def.h"		/* eigenes Headerfile */
	/* Bereich b) */
	void VAR Beispiel(ParamBlk FAR *parm)
	{
		*/ hier der Funktionsinhalt inkl. Rückgabe */
	}
	/* siehe später zu eigener Header-Datei */
	FOXPRO Routine2(PARAMETERS)
	{
	  Boolean ok = TRUE;
	  char FAR *parm1;
	  if (PCOUNT == 1 && ISCHAR(1))
	  {
	    /* weiterer Quellcode */
	  }
	  else
	  {
	    ok = FALSE;
	  }
	  RETL(ok);
	}
	/* Bereich c) */
	FoxInfo myFoxInfo[] = {
		{ "INFOXPRO1", (FPFI) Beispiel, 1, ".?" },
		{ "INFOXPRO2", (FPFI) Routine2, 2, ".?,.?" }};
	/* INFOXPRO1, INFOXPRO2 sind die Funktionsnamen, die in FoxPro
	   aufgerufen werden.
	   (FPFI) Beispiel,(FPFI) Routine2 sind die Adressen auf die
	   internen Funktionsnamen, exakt so, wie sie definiert wurden.
	   1, 2 spezifizieren die Anzahl der Parameter, die durch den
	   Funktionsaufruf übergeben werden.
	   ".?", ".?,.?" spezifizieren die Parametertypen. Mögliche
	   Typen sind C, N, I, D, L, R und ?. Ich nehme immer .? (der
	   Punkt bedeutet optional), da ich die Überprüfung der Para-
	   meter im Quellcode vornehme. */
 	/* Bereich d) */
	FoxTable _FoxTable = {
		(FoxTable FAR *)0,
		sizeof(myFoxInfo)/sizeof(FoxInfo),
		myFoxInfo};

 

/* Bereich a) */

#include <windows.h> /* Headerfile zum SDK */
#include <pro_ext.h> /* Headerfile zum LCK */
#include "my_def.h" /* eigenes Headerfile */


/* Bereich b) */

void VAR Beispiel(ParamBlk FAR *parm)
{
*/ hier der Funktionsinhalt inkl. Rückgabe */
}


/* siehe später zu eigener Header-Datei */

FOXPRO Routine2(PARAMETERS)

{

Boolean ok = TRUE;

char FAR *parm1;


if (PCOUNT == 1 && ISCHAR(1))
{
/* weiterer Quellcode */

}

else

{

ok = FALSE;

}

RETL(ok);

}

 

/* Bereich c) */
FoxInfo myFoxInfo[] = {

{ "INFOXPRO1", (FPFI) Beispiel, 1, ".?" },

{ "INFOXPRO2", (FPFI) Routine2, 2, ".?,.?" }};


/* INFOXPRO1, INFOXPRO2 sind die Funktionsnamen, die in FoxPro
aufgerufen werden.
(FPFI) Beispiel,(FPFI) Routine2 sind die Adressen auf die
internen Funktionsnamen, exakt so, wie sie definiert wurden.
1, 2 spezifizieren die Anzahl der Parameter, die durch den
Funktionsaufruf übergeben werden.
".?", ".?,.?" spezifizieren die Parametertypen. Mögliche
Typen sind C, N, I, D, L, R und ?. Ich nehme immer .? (der
Punkt bedeutet optional), da ich die Überprüfung der Para-
meter im Quellcode vornehme. */


/* Bereich d) */

FoxTable _FoxTable = {

(FoxTable FAR *)0,

sizeof(myFoxInfo)/sizeof(FoxInfo),

myFoxInfo};


Wie Sie oben sicherlich festgestellt haben, wird eine Funktion mit void FAR deklariert. Dies bedeutet, daß diese Funktion selbst nichts zurückgibt. Dann folgt der von Ihnen zu vergebende Funktionsname. Anschließend wird, in Klammern gesetzt, immer nur ein Parameter, und zwar die Struktur namens ParamBlk FAR *parm, übergeben. Diese Struktur enthält eine Reihe von Informationen, wie z.B. die Anzahl übergebener Parameter, die Parametertypen, Inhalte oder Adressen (bei Zeichenketten), ob Parameter per Referenz oder Wert übergeben wurden usw..

Dann folgt der Funktionskörper - die zu deklarierenden Variablen, die Parameterüberprüfungen, die auszulösenden Aktionen, evtl. eine Rückgabe an Foxpro. Nun werden aber die C-Programmierer sagen, wenn ich eine Funktion mit void deklariere, kann ich nichts zurückgeben. Das ist so richtig, denn jetzt setzt das LCK mit seinem eigenen Funktionsumfang ein und bietet eine Reihe von _Ret....() Funktionen, um ein Ergebnis an FoxPro zurückzugeben.

Ich möchte den Aufbau einer C-Funktion anhand eines ersten praktischen Beispieles aufzeigen. Alle C-Funktionen, die das LCK zur Verfügung stellt, sind in fett dargestellt. Das Beispiel demonstriert die Möglichkeit, in FoxPro praktisch gänzlich auf Macroeinsätze zu verzichten und damit auch lokale ON ERROR-Routinen überflüssig zu machen. Dies wird alles durch eine API-Funktion im LCK mit dem Namen _Execute() ermöglicht.

/* Bereich a) */
#include <pro_ext.h>
/* Bereich b) */
//-------------------------------------------------------------------------
// Funktion : NullTerminate()
// Parameter: Value-Struktur
// Rückgabe : .T. wenn String NULL-terminiert wurde, .F. wenn nicht
//
// Diese Funktion wird ausschließlich intern benutzt, um einen durch FoxPro
// übergebenen String mit einem NULL-Terminator zu versehen
//-------------------------------------------------------------------------
int NullTerminate(Value FAR *cVal)
{
  int ok = TRUE;  /* assume Terminierung ok */
  if (!_SetHandSize(cVal->ev_handle,
    cVal->ev_length + 1)) /* Reallocierung nicht möglich? */
    ok = FALSE;   /* ist nicht ok */
  else
    ((char FAR *) _HandToPtr(cVal->ev_handle))[cVal->ev_length] = NIL;
  return(ok);
}
 
//-------------------------------------------------------------------------
// Funktion : Execute()
// Parameter: cBefehl = auszuführender Befehl als String
// Rückgabe : .T. wenn Befehl ausgeführt wurde, .F. wenn nicht
//
// Diese Funktion ersetzt die Macrosetzung von Befehlen. Desweiteren
// werden in bestimmten Situationen die Setzung von lokalen Error-
// Routinen überflüssig.
//-------------------------------------------------------------------------
void FAR Execute(ParamBlk FAR *parm)
{
  int ok = 0;     /* assume Exec nicht ok */
  char FAR *exec;
  int result;
  if ((parm->pCount) == 1 &&
      (parm->p[0].val.ev_type == ´C´))    /* richtigen Param. übergeben */
  {
    if (NullTerminate(&parm->p[0].val))   /* String terminiert? */
    {
      _HLock(parm->p[0].val.ev_handle);   /* Handle festhalten */
      /* hole String-Adresse */
      exec = ((char FAR *) _HandToPtr(parm->p[0].val.ev_handle));  
      /* String terminieren */
      exec[parm->p[0].val.ev_length] = `\0`;
      result = _Execute(exec);      /* führe Befehl aus */
      _HUnLock(parm->p[0].val.ev_handle);   /* Handle freigeben */
      if (!result)  /* alles ok? */
ok = 1;
    }
  }
  _RetLogical(ok);  /* gebe Resultat zurück */
}
/* Bereich c) */
FoxInfo myFoxInfo[] = {
		{ "EXECUTE", (FPFI) Execute, 1, ".?" }}
/* Bereich d) */
FoxTable _FoxTable =
	  {
		(FoxTable FAR *)0,
		sizeof(myFoxInfo)/sizeof(FoxInfo),
		myFoxInfo
	  };

Diese Funktion wird nun in eine FLL-Datei umgewandelt und kann dann über SET LIBRARY TO <libName> für FoxPro zugänglich gemacht werden.

Ein Nachteil für nicht unbedingt versierte C-Programmierer ist zum einen die Lesbarkeit des obigen Quellcodes. Aber auch durch die Strukturen erhöht sich evtl. die Fehlerträchtigkeit bei dem Schreiben eigener C-Funktionen.

Ich habe mir eine durchaus sinnvolle Sache von Clipper und dessen Extend-System angeeignet, und eine Header-Datei entwickelt, die Definitionen enthält, um mir das Schreiben und das Handling von C-Funktionen zu erleichtern. Eine ähnliche Header-Datei wird mit dem Clipper-Compiler automatisch mit ausgeliefert. Vielleicht kommt Microsoft zukünfigt auf den Gedanken, für das LCK ähnliches zu tun.

Ich möchte Ihnen diese Definitionen einmal kurz erläutern und dann das obige Beispiel nochmals darstellen, aber mit den vorgestellten Definitionen.

// Definitionen für den Funktionskopf
#define FOXPRO  void FAR
#define PARAMETERS      ParamBlk FAR *parm
// Definitionen für Stringbehandlung
/* Übergabe der Value-Struktur an eine Funktion */
#define PARMSTRING(n)   (&parm->p[n-1].val)
/* Erweiterung des Strings um <b> Bytes */
#define NEW_SIZE(n, b)  (_SetHandSize(parm->p[n-1].val.ev_handle, \
						   parm->p[n-1].val.ev_length + b))
/* Ermittlung der Stringlänge */
#define STR_LEN(n)      parm->p[n-1].val.ev_length
/* Stringhandle locken */
#define LOCKHANDLE(n)   (_HLock(parm->p[n-1].val.ev_handle))
/* Stringhandle freigeben */
#define UNLOCKHANDLE(n) (_HUnLock(parm->p[n-1].val.ev_handle))
// Definitionen für Parameter
/* ermittelt Anzahl der Parameter */
#define PCOUNT  (parm->pCount)
/* Parameter ein String */
#define ISCHAR(n)       (parm->p[n-1].val.ev_type == 'C')
/* Parameter numerisch (float) */
#define ISNUM(n)(parm->p[n-1].val.ev_type == 'N')
/* Parameter numerisch (integer) */
#define ISINT(n)(parm->p[n-1].val.ev_type == 'I')
/* Parameter ein Datum */
#define ISDATE(n)       (parm->p[n-1].val.ev_type == 'D')
/* Parameter logisch */
#define ISLOG(n)(parm->p[n-1].val.ev_type == 'L')
/* Parameter ein Memo(feld) */
#define ISMEMO(n)       (parm->p[n-1].val.ev_type == 'M')
/* Parameter per Referenz übergeben */
#define ISBYREF(n)      (parm->p[n-1].loc.l_type == 'R')
/* Parameter ein Array */
#define ISARRAY(n)      (parm->p[n-1].loc.l_subs != 0)
/* Anzahl der Arrayelemente (Anzahl Zeilen) */
#define ALENGTH(n)      (_ALen(parm->p[n-1].loc.l_NTI,AL_SUBSCRIPT1))
// Parameterinhalte holen 
/* String holen */
#define PARC(n) ((char FAR *) _HandToPtr(parm->p[n-1].val.ev_handle))
/* Integerwert holen */
#define PARL(n) (parm->p[n-1].val.ev_long)
/* numerischen (float) Wert holen, aber auch Datum */
#define PARD(n) (parm->p[n-1].val.ev_real)
/* logischen Wert holen */
#define PARLOG(n)       (parm->p[n-1].val.ev_length)
 // Rückgaben an FoxPro 
/* Rückgabe eines logischen Wertes */
#define RETL(n) (_RetLogical(n))
/* Rückgabe eines numerischen Wertes */
#define RETLONG(n)      (_RetInt(n, 10))
/* Rückgabe einer Zeichenkette */
#define RETSTRING(c)    (_RetChar(c))

Und nun die Funktion Execute() nochmals mit den Definitionen, wobei in der eigenen Headerdatei noch stehen würde:

#define NIL     '\0'
typedef int Boolean;
FOXPRO Execute(PARAMETERS)
{
  Boolean ok = FALSE;       /* assume Exec nicht ok */
  char FAR *exec;
  int result;
  if (PCOUNT == 1 && ISCHAR(1))     /* richtigen Param. übergeben? */
  {
    if (NullTerminate(PARMSTRING(1)))       /* String terminiert? */
    {
      LOCKHANDLE(1);/* Handle festhalten */
      exec = PARC(1);       /* hole String-Adresse */
      exec[STR_LEN(1)] = NIL;       /* String terminieren */
      result = _Execute(exec);      /* führe Befehl aus */
      UNLOCKHANDLE(1);      /* Handle freigeben */
      if (!result)  /* alles ok? */
ok = TRUE;
    }
  }
  RETL(ok); /* Resultat zurückgeben */
}

Dieser Quellcode ist m.E. nun einfacher zu lesen, schneller zu schreiben und auch geringer in seiner Schreibfehleranfälligkeit .

In einem FoxPro-Programm können Sie nun mit dieser Funktion jeden FoxPro-Befehl ausführen, ohne das Macro & einzusetzen. Zusätzlich bekommen Sie als Resultat noch zurück, ob die Ausführung des Befehles erfolgreich war oder nicht.

Beispiel: Der Indexaufbau Ihrer Tabellen wird datenbankgesteuert durchgeführt. Bisher wäre es es so, daß Sie den Inhalt eines Indexausdruckfeldes in eine Variable speichern und den Index z.B. wie folgt aufbauen würden

Mit der Funktion Execute() können Sie den Index wie folgt aufbauen:

Ich möchte Ihnen an einem zweiten praktischen Beispiel den Einsatz von API-Routinen durch das
LCK aufzeigen.

Geben Sie einmal im FoxPro-Befehlsfenster dir a: ein und lassen das Laufwerk A leer. Es erscheint das wohlbekannte Windows-Systemfenster, daß Sie darauf aufmerksam macht, Laufwerk nicht bereit - Abbrechen / Wiederholen. Manchmal müssen Sie bis zu 5x den Button Abbrechen bestätigen, bis Windows so gnädig ist, das Systemfehlermeldungsfenster zu schließen.

Wenn nun in Ihrer Applikation ein Laufwerkszugriff erfolgen soll und das Laufwerk ist nicht bereit, erhalten Sie auch erst einmal das hübsche Windowsfenster, bevor Sie eine ON ERROR-Routine oder eine eigene Abfrage aktivieren können.

Das Windows-SDK bietet eine Vielzahl von Funktionen an (über 700), mit der Windows programmiert werden kann. Unter anderem gibt es auch eine Funktion, der diesen kritischen Errorhandler aktiviert oder deaktiviert. Diesen machen wir uns nun in einer C-Routine zu eigen, um in unseren Applikationen nach Bedarf den kritischen Errorhandler von Windows ein- oder auszuschalten.

#include <pro_ext.h>
#include <windows.h>
//-------------------------------------------------------------------------
// Funktion : WinError()
// Parameter: lErrMode = .T. Systemfenster bei kritischen Fehlern ausgeben
//       .F. kein Fenster öffnen
// Rückgabe : immer .T.
//
// Mit dieser Funktion kann vermieden werden, daß Windows bei kritischen
// Fehlern (z.B. nicht bereites Laufwerk) sein wohlbekanntes Systemfenster 
// öffnet.
//-------------------------------------------------------------------------
FOXPRO WinError(PARAMETERS)
{
  UINT nParm = 0;
  Boolean flag = TRUE;
  if (PCOUNT == 1 && ISLOG(1))      /* Parameter übergeben? */
    flag = (Boolean) PARLOG(1);     /* hole diesen */
  nParm = (flag) ? 0 : SEM_FAILCRITICALERRORS; /* welcher Modus */
  /* das ist die Funktion aus dem Windows SDK */
  SetErrorMode(nParm);      /* setze Fatal Handler */
  RETL(TRUE);       /* immer .T. */
}
FoxInfo myFoxInfo[] = {
		{ "WINERROR", (FPFI) WinError, 1, ".?" }}
FoxTable _FoxTable =
	  {
		(FoxTable FAR *)0,
		sizeof(myFoxInfo)/sizeof(FoxInfo),
		myFoxInfo
	  };

Nun können Sie, wenn eine Library mit der Funktion erstellt und in Ihrer Applikation angemeldet wurde, die Funktion einsetzen, um eigene Abbruchabfragen (mit ansprechenden Fenstern) zu generieren.

Beispiel: Der User Ihrer Applikation will auf ein Diskettenlaufwerk zugreifen - es muß überprüft werden, ob ein Lese-/Schreibzugriff möglich ist.

Diese beiden praktischen Beispiele sollen Ihnen aufzeigen, welche enormen Möglichkeiten Ihnen
mit der Schnittstelle von FoxPro zu C gegeben sind, Dinge in FoxPro auszuführen, zu denen
FoxPro von Hause aus nicht in der Lage ist.

Ich habe die Möglichkeiten des LCK genutzt, um eine Library (Cl-toFox) zu entwickeln, die es mir unter anderm ermöglicht, meine Programme, die ich unter Clipper 5.x geschrieben habe, erheblich schneller und einfacher auf die Windowsebene mit FoxPro zu adaptieren. Dazu war es notwendig, ein Großteil Clipperfunktionen, die FoxPro nicht kennt, zu entwickeln. Eine Anzahl der Funktionen wären auch mit der FoxPro-Sprache möglich gewesen. Da aber auch Arbeitsge-schwindigkeit nicht außer Betracht zu lassen ist, ist die Mehrzahl in C entwickelt worden.


 

3. Testen von Funktionen aus der Library

Der Test erstellter Funktionen in C gestaltet sich auch nicht viel schwieriger als das Debuggen Ihrer FoxPro-Routinen. Wenn Sie die C-Compiler von Microsoft einsetzen, wird ein interessantes Programm namens CVW.EXE mitgeliefert. CVW ist der CodeView-Debugger, speziell für Windows. Um nun eine Funktion austesten zu können, müssen Sie an der Position, ab der das Debugging beginnen soll, im C-Quellcode die Zeile

mit aufnehmen. Sie kompilieren dann Ihren Quellcode mit den zusätzlichen Compilerschaltern /Zi und /Od. Der Linker wird mit dem zusätzlichen Schalter /CO aktiviert. Ist die Library dann fehlerfrei erstellt worden, können Sie nun Ihre Funktion austesten.

Laden Sie dazu zuerst CodeView über den Programm-Manager - Menü Datei -> Ausführen durch Eingabe von CVW FOXPROW. Der CodeViewer wird nun geladen. Danach drücken Sie die Funktionstaste F5 zum Starten von FoxPro. Ist FoxPro geladen, geben Sie im Befehlsfenster ein SET LIBRARY TO <meinelib>. Anschließend geben Sie dann den Funktionsaufruf der auszutestenden Routine in das Befehlsfenster ein. CodeView wird nun aktiviert und bleibt, wenn CodeView den Quellcode der Library gefunden hat, in der Zeile _BreakPoint() stehen. Konnte CV den Quellcode nicht finden, öffnet sich ein Dialogfenster, in dem Sie das Verzeichnis und den Namen Ihres Library-Quellcodes eingeben. Ist das Registerfenster noch nicht geöffnet, öffnen Sie dieses mit Alt-7. Im Registerfenster wird nun der Instruktionspointer IP um 1 erhöht, damit CV über die Anweisungszeile _BreakPoint() steppen kann. Danach können Sie die C-Routine Zeile für Zeile abarbeiten und eventuell Fehler oder Probleme beobachten, um sie später dann zu beheben.

Ist Ihre Debugsitzung beendet, schließen Sie zuerst FoxPro mit Alt-F4 oder QUIT oder...

Dann wird wieder das Debug-Fenster von CV angezeigt. Dieses schließen Sie ebenfalls mit Alt-F4 oder über das Menü File -> Exit.


 

4. Für wen ist das LCK geeignet

Das LCK ist für Sie auch dann interessant, wenn Sie zwar die Syntax von C einigermaßen beherrschen, sich aber nicht mit dem Windows-SDK auseinandersetzen wollen. Sie können vom LCK profitieren, wenn Geschwindigkeit in bestimmten Situationen gefragt ist. Gerade wenn viel in FOR-Schleifen abgearbeitet wird, könnte C dramatische Geschwindigkeitszuwächse für Anwendungen bringen.

Auch wenn Sie nicht unbedingt der Profi in C sind, können Sie vielleicht doch indirekt vom LCK partizipieren, wenn Sie Bibliotheken von Drittanbietern einsetzen. Die meisten der Tools sind mit dem Construction Kit entwickelt worden (unter anderem auch das Connectivity Kit aus der Professional FoxPro-Version).

Der Erweiterung von FoxPro sind durch das LCK kaum noch Grenzen gesetzt. Das macht FoxPro interessant auch für die, die bisher auf Compiler wie Clipper, Clarion und andere gesetzt haben und noch setzen. Auch diese Gruppe von Programmierern wird über kurz oder lang, da in ihren Bereichen noch keine adäquate xBase-Sprache unter Windows existiert, mit FoxPro konfrontiert. Dann ist es gut zu wissen, daß Microsoft die Tradition der offenen Architektur auch für FoxPro fortsetzt und Ihnen alle Freiheiten läßt, FoxPro nach Ihren Bedürfnissen zu erweitern.

Ob Sie nun selbst mit dem API und LCK arbeiten werden, oder ob Sie eventuell auf Dritt-Libraries zurückgreifen werden, ich wünsche Ihnen viel Glück.


FoxPro Library Construction Kit
(c)1994 Kurt Zander