Session D-DEVE

Die VFP7 Entwicklungsumgebun

Joachim Hilgers
Hicosoft GmbH


Was ist neu?

VFP7 wird über deutliche Verbesserungen im Bereich der Benutzeroberfläche (IDE) verfügen. Microsoft hat sich hier anscheinen endlich einmal wieder nach den Anwendern (hier den Entwicklern) gerichtet. Die Erweiterungen machen VFP deutlich komfortabler und bedeuten zugleich von der Bedienung her eine stärkere Annäherung an z.B. VB. Damit wird der Fuchs besser Visual Studio integriert und schützt andere Entwicklern vor dem bisher sonst unvermeidbaren Kulturschock.

Intellisense

Intellisense ist eine der Fähigkeiten von VB, auf die VFP-Entwickler bisher neidisch sein konnten, und zwar weil man in VFP viele Sachen wissen oder suchen muss, die einem VB beim Codieren automatisch vorschlagen würde. So gehört es z.B. nicht unbedingt zu jedermanns Grundwissen beim programmatischen Erzeugen einer ComboBox ad hoc zu wissen, welcher der 10 RowSorceTypes für ein SQL-Statement einzutragen ist. Intellisense unterstützt dabei im einzelnen wie folgt:

Members List

Die bisher nur über die Rechte Maustaste verfügbare Funktion "Object List" wird jetzt quasi automatisch ausgeführt. Während der Eingabe erscheint dabei eine Drop-Down-Liste, die zum aktuellen Kontext alle Objekte, deren Methoden, Events, und Properties anzeigt. In dieser Liste kann navigiert werden. Die Eingabezeile wird dann entsprechend ergänzt. Diese Infos werden auch für COM-Objekte angeboten, womit u.a. der Zugriff auf die MS-Office Programme drastisch vereinfacht wird.

 

Abbildung 1: Intellisense - Auto List Members

Quick Info

Zeigt eine Kurzinfo zum momentanen Kommando, Methode, Event oder Property an. Darin enthalten sind dann ggf. auch Angaben zu Parametern und den dabei erwarteten Datentypen.

Values List

Für Properties wird eine Liste der verfügbaren Werte und deren Bedeutung angezeigt.

Abbildung 2: Intellisense - Values List

Tabellen Infos

Wenn Tabbellenfelder angesprochen werden, werden - falls vorhanden - automatisch die entsprechenden Kommentare aus dem Datenbankcontainer angezeigt.

Die für Intellisense benötigten Informationen entnimmt VFP entweder der Registry (für COM- und ActiveX-Komponenten ) oder der neuen FOXCODE.DBF. Mit Hilfe dieser schon im Auslieferungszustand ziemlich umfangreichen Steuertabelle werden zu im Editor eingegebenen Befehlen zugehörige Tooltips (z.B. Parameter-Infos) eingeblendet. Außerdem können über frei konfigurierbare Schlüsselworte beliebige Codeblöcke in den Quellcode eingefügt werden, z.B. "DC" für eine DO CASE - Struktur. Diese Funktionalität entspricht einem Teil der des schon länger frei verfügbaren Tools CEE (Cobb Editor Extensions). Die FOXCODE.DBF kann um Einträge für eigene Programme/Klassen erweitert werden.

Zur Unterstützung von Intellisense steht außerdem eine Erweiterung der Definition von Methoden, Funktionen und Prozeduren zur Verfügung:

    FUNCTION | PROCEDURE Name ( [parm [AS type]]) [AS type] ;
          [HelpString  helpstring]  

    oder konkreter

    function myEval ;
       (tcCommand as string) ;
       as variant ;
       helpstring "Hier wäre Ihre Hilfe"

Die Funktion "myEval" erwartet damit einen String als Parameter und liefert einen beliebigen Datentyp (variant) zurück.

Diese Erweiterung wird von Microsoft als Strong Typing bezeichnet. Da dies jedoch nicht zur Laufzeit erzwungen wird, sondern nur "primär zur Designzeit verfügbar ist", sollte man dies nicht mit den entsprechenden Features anderer Sprachen verwechseln. Der Sinn ist hier eher darin zu sehen, die gewünschten Eigenschaften des entsprechenden Interfaces (anderen) Entwicklern zu publizieren. Somit können dann auch endlich VB-Entwickler die für sie verfügbaren VFP-COM-Komponenten etwas schmerzfreier mittels Intellisense benutzen.

Verbesserter Editor

Der Editor wurde u.a. um die folgenden Features erweitert:

    wahlweises Benutzen von Blanks oder Tabulatoren beim Einrücken

    Konfigurieren des Kommentar-Strings, ohne in der Registry ändern zu müssen.

    Hyperlinks im Code und in Kommentaren (z.B. um zugehörige externe Dokumentation direkt aufrufen zu können)

    Breakpoints können direkt durch das Klicken auf den Rand des Codefensters gesetzt werden (erspart das Benutzen des RightClick-/Kontext Menüs)

    Das Setzen von Bookmarks und Shortcuts (Textmarken, s.a.: Task Liste ) um schnell zu bestimmten Codezeilen zu wechseln. Hierbei sind Bookmarks nur temporäre Markierungen, Shortcuts hingegen permanente.

Container bearbeiten

Das Bearbeiten verschachtelter Container mittels mehrfachem RightClick + "Bearbeiten" oder durch navigieren im Eigenschaften-Fenster wird stark vereinfacht: Control-Click schaltet direkt in den Bearbeiten-Modus des entsprechenden Elements, Control-Shift-Click öffnet sofort ein Code-Fenster.

Andockbare Fenster

Wer schon einmal mit den andockbaren Fenstern im Debugger gekämpft hat, wird die Einsetzbarkeit dieses Features mit Sicherheit zunächst einmal in Frage stellen. Neu ist jetzt, dass jetzt alle Fenster andockbar sind, sowie die Möglichkeit ein Fenster in einem anderen anzudocken, das dann automatisch entsprechende Tabs zum auswählen der einzelnen Fenster enthält.

Abbildung 3: Andockbare Fenster

Task Liste / Shortcuts

Ab und zu soll es bei Programmierern vorkommen, dass sie einzelne Codeteile nicht sofort vollständig ausprogrammieren. Diese Stellen werden zumeist in der Form eines Kommentars mit einer eindeutigen Kennung abgelegt wie z.B. "*!! <datum> Hier unbedingt noch ...".

Das vollständige Nachhalten aller dieser Stellen, z.B. um ungewolltes Ausliefern eines noch nicht fertigen Programmteils zu verhindern, war bisher zumeist etwas aufwendig. Die neue Task-Liste ermöglicht es im Quellcode Shortcuts zu setzen, die dann einschließlich des in der Zeile befindlichen Kommentars in einer Tabelle abgelegt werden.

Abbildung 4: Task Liste

Über die Shortcuts kann man sehr einfach per Mausklick an die entsprechende Stelle im Sourcecode springen.

Außerdem kann man innerhalb eines Projektes recht einfach den Überblick über die ToDos behalten, was  z.B. bei einem Kommentar wie "*!! So auf keinen Fall ausliefern..." ein offensichtlicher Vorteil ist.

Der neue Befehl

EDITSOURCE(nShortcutID | cFileName [,nLine] [,cClassName] [,cProcName])

öffnet den Editor direkt an der im Shortcut definierten Stelle oder eine beliebige andere Sourcedatei direkt in der angegebenen Zeile.

Object  Browser

VFP erhält endliche einen Object Browser für COM-Objekte, ähnlich dem, der in VB enthalten ist. Der Object Browser zeigt diverse Infos zu COM-Objekten an, und zwar ähnlich wie der FoxPro Class Browser zu FoxPro Klassen. Zusätzlich werden jedoch u.a. Konstanten angezeigt. Hiermit dürfte für einige Entwickler die Notwendigkeit entfallen VB nur wegen des Browser installieren zu müssen.

Abbildung 5: Object Browser

Die individuelle Standard-Entwicklungsumgebung

Jeder Entwickler, der sich einmal „seine“ Umgebung zusammengebaut und sich an deren Komfort gewöhnt hat, dem kommt in der Regel ein unverändertes VFP ziemlich „nackt“ vor, und die Umgebung eines zugekauften Frameworks zu spezialisiert oder limitiert.

Um das zu verhindern, kann und sollte die IDE (Integrated Development Environment = Die VFP-Benutzeroberfläche) gleich beim Start angepasst werden, und zwar durch ein automatisch ausgeführtes Programm, das die entsprechenden Einstellungen vornimmt.

_STARTUP

Die hierfür möglichen Verfahren sind – neben dem manuellen Aufruf nach jedem Start - Einträge in der CONFIG.FPW 

COMMAND=<DO Mein_StartProgramm>

und

_STARTUP=<Mein_StartProgramm>.

Außerdem kann der Name eines zu startenden Programms als Kommandozeilenparameter übergeben werden. Alternativ zur CONFIG.FPW, die vom Fuchs zunächst im Arbeits-, dann im HOME()-Verzeichnis und schließlich im Suchpfad gesucht wird, kann auch eine beliebige benannte andere Konfigurationsdatei als Kommandozeilenparameter übergeben werden: 

–c<MeineConfigDatei>

Außerdem kann noch auf Betriebssystem-Ebene eine entsprechende Datei festgelegt werden per :

SET FOXPROWCFG=<MeineConfig.FPW>

_STARTUP ist eine VFP-Systemvariable (PUBLIC) und als solche auch später jederzeit verfügbar. Einmal in der CONFIG.FPW initialisiert, kann die individuelle IDE damit jederzeit mittels DO (_STARTUP) wieder neu aufgebaut werden, was z.B. nach dem gelegentlich notwendigen CLEAR ALL u.U. einfacher als ein Neustart ist.

Das Entwicklermenü

Menüs bieten sich ideal als Basis für eigene Erweiterungen an. Mit Ihnen können Hotkeys definiert werden, Sie bieten eine Oberfläche, über die man seltener benutzte Funktionen wiederfinden kann, sie können komplexen Code enthalten und vor allem sind sie CLEAR ALL–resistent und dies im Gegensatz zu den allgemein üblichen PUBLIC-Dienstobjekten (wie z.B. Superclass. CEE usw.).

Das Menü selbst sollte möglichst einfach aufzurufen sein, also als erster Menü-Pad (Position „Before“) und/oder mit einem leicht erreichbaren Hotkey (ich benutze z.B. {Alt-#}).

Falls in dem Menü rechnerabhängige Pfade benutzt werden sollen, z.B. weil mehrere Entwickler das Menü einsetzen (die natürlich mit anderen Pfaden und Laufwerken arbeiten <g>), bietet sich die Benutzung einer Include-Datei oder alternativ einer INI-Datei an. Z.B. benutzt das „Woody Menü“ (Shareware von Jürgen "Woody" Wondzinski) die schon erwähnte CONFIG-Datei, die sich einfach per SYS(2019) ermitteln lässt. Hier sollte man allerdings vorsichtig sein, da u.a. einzelne Frameworks, die - sinnvoller weise - ihre eigene Entwicklungsumgebung mitbringen, eben auch SYS(2019) benutzen.

Um das Menü einfach bearbeiten zu können – welchem Entwickler fallen schließlich nicht andauernd Verbesserungen ein? – gehört auch eine entsprechende „Bearbeite Mich“ -Funktion hinein.

Abbildung 6: Entwicklermenü bearbeiten

"Bearbeiten" schreibt bei mir die folgenden Zeilen in das Befehlsfenster:

    *modi comm C:\FOX50\A\HICOMENUE\HicoMProc.PRG
    *modi file C:\FOX50\A\HICOMENUE\hico.h
    *modi file C:\FOX50\A\HICOMENUE\hico.err
    *modi file C:\FOX50\A\HICOMENUE\RE_INIT.PRG
    *modi file C:\FOX50\A\HICOMENUE\ver.txt
    *do C:\FOX50\A\HICOMENUE\hico.mpr
    modi menu C:\FOX50\A\HICOMENUE\hico

Damit lassen sich die üblichen Wartungsarbeiten schnell und komfortabel erledigen

Cleanup vs. Prozedurdatei

Funktionen, die von mehreren Menüpunkten aus benutzt werden sollen, wie z.B. das Auslesen von INI-Dateien und Registry können grundsätzlich im CLEANUP-Code des Menüs untergebracht werden, der dann wie eine Prozedurdatei genutzt werden kann. Eine externe Prozedurdatei kann anstelle des CLEANUP-Codes nur bedingt eingesetzt werden, da diese dann per SET PROCEDURE geöffnet werden und bleiben muss falls Funktionen ausgeführt werden sollen (z.B. ? MeineFunktion() in MeineProzedurdatei geht nicht!).

Ich benutze lieber eine externes PRG, das alle benötigten Prozeduren und auch einzeilige Kommandos enthält und zwar aus den folgenden Gründen:

    SET PROCEDURE wird von häufig von Zusatztools und Frameworks zurückgesetzt und die benötigten Prozeduren sind dann nicht mehr verfügbar

    Code im CLEANUP des Menüs wird bei einem Laufwerkswechsel ( CD / SET DEFAULT TO) in der Regel nicht mehr gefunden

    Wartungsarbeiten an einer externen Prozedurdatei sind deutlich einfacher als innerhalb eines Menüs

Der Aufruf  der einzelnen Funktion erfolgt im Menü mittels

    do MODIFY_VERSION_TXT in HMProc

Wobei HMProc ( HicoMProc.PRG auf der Begleit-CD ) eine Konstante aus der zum Menü gehörenden Include-Datei ist.

MODIFY /CREATE ...

Der Zugriff auf häufig genutzte Funktionen lässt sich über Hotkeys beschleunigen:

Editorfunktionen

Der VFP-Editor läßt sich recht einfach um eigene Tastenkürzel erweitern. Wer z.B. {CTRL-Y} zum Löschen einer Zeile einmal schätzen gelernt hat, wird dieses Kürzel vermissen:


VIEW MEMORY/STATUS/OBJECTS...

Die diversen LIST/DISPLAY-Befehle können sehr einfach in Textdateien umgeleitet und dann mit dem VFP-Editor angezeigt, sowie sofort ausgedruckt werden


Den hierfür notwendigen Code finden Sie im Cleanup des beiliegenden Menüs. Im Wesentlichen passiert dort folgendes:

    list status to (lcOutputFile) noconsole
    activate window command
    keyboard "{ctrl-end}" + "modi file " +lcOutputFile + "{enter}"

Dadurch, dass der „MODIFY FILE ...“-Befehl über das Befehlsfenster geschrieben wird, kann der Benutzer die Infos später erneut öffnen (bis sie überschrieben wird). Gleichzeitig erhält er einen Hinweis, in welcher Datei die Info denn abgelegt wurde.

Hilfe

Vorhandene Hilfedateien und Beispiele werden erfahrungsgemäß nur dann eingesetzt, wenn sie einfach aufrufbar sind. Aufrufe für verschiedene Hilfedateien und Beispiele gehören also auch in das Menü.

Die VFP3-Hilfe ist in diesem Menü mit enthalten, da viele (alte) Befehle in den VFP Versionen ab 5 in der Hilfe nur noch mit dem Kommentar „Aus Kompatibilitätsgründen enthalten“ beschrieben werden, und da diese Befehle in dieser älteren Hilfedatei noch komplett beschrieben sind.

Mit dem neuen HTML-Hilfesystem in VFP, insbesondere den im Visual Studio mitgelieferten Hilfen für alle VS-Produkte könnte über dieses Menü dann auch gezielt auf die Hilfe zu VFP oder die anderer Sprachen umgeschaltet werden.

:

Bei vollständig installiertem Visual Studio Hier sollte man ggf. standardmäßig beim Start „nur“ die VFP-Hilfe aktivieren – das Hilfesystem ist dann merklich schneller und beim Suchen werden nicht andauernd auch die Treffer der anderen Sprachen mit angezeigt:

    set help to (left(_samples,at("SAMPLES",_samples)-1)+"FOXHELP.CHM")

Standard SETings

Bevor irgendwelcher anderer Code ausgeführt wird, sollten die diversen SET-Einstellungen den eigenen Wünschen entsprechend eingestellt werden. Hier empfiehlt es sich, so ziemlich alle SETs einzustellen. Zum einen kann dieser Code auch in den eigenen Projekten benutzt werden, zum anderen kann man so sicher gehen, daß die gewünschten eigenen Default-Werte gelten. Zum anderen gilt es zu beachten, daß VFP diverse SETs aus der Registry übernimmt. So kann es z.B. passieren, dass eine Runtime-Umgebung mit dem SET PATH einer zuvor beendeten VFP-Instanz arbeitet...

Eine vollständige Liste aller momentanen SET-Einstellungen kann man sehr einfach erzeugen, indem man im Menü über "Tools" die Optionen-Einstellungen öffnet und bei aktiver Seite "General/Allgemein" bei gedrückter Shift-Taste den OK-Knopf anklickt. Alle SETs werden dann in das Befehlsfenster geschrieben.

Intellidrop-Einstellungen

Ein „beliebter“ Fehler ist das unbemerkte Benutzen von Klassen, die eigentlich gar nicht zum aktuellen Projekt gehören. Dieser Fehler führt dann zu Effekten wie:

·         Die Sourcen laufen auf dem Rechner eines anderen Entwickler nicht

·         Die APP/EXE ist plötzlich viel größer

·         Nach dem Entfernen eines anderen Projektes von der Platte läuft nichts mehr

·         Die Eingabe-Controls verhalten sich unterschiedlich

VFP legt seine unter „Tools“-„Options“-„Forms“ und „Intellidrop“ vorzunehmenden Voreinstellungen für Form- und Controls-Klassen in der Registry ab und zwar unter:

    Software\Microsoft\VisualFoxPro\6.0...
    ... \Options:FormSetClass=""
    ... \Options:FormSetLib=""
    ... \Options:FormsClass="BaseForm"
    ... \Options:FormsLib="base.vcx"

    sowie unter

    Software\Microsoft\VisualFoxPro\6.0\Options IntelliDrop...
    ... \FieldTypes\Character:ClassLocation="base.vcx"
    ... \FieldTypes\Character:ClassName="baseTextBox"
    ... \FieldTypes\Character (binary):ClassLocation="base.vcx"
    ... \FieldTypes\Character (binary):ClassName="baseTextBox"
    ... \FieldTypes\Currency:ClassLocation="base.vcx"
    ... \FieldTypes\Currency:ClassName="baseTextBox"
    ... \FieldTypes\Date:ClassLocation="base.vcx"
    ... \FieldTypes\Date:ClassName="baseTextBox"
    ... \FieldTypes\DateTime:ClassLocation="base.vcx"

Das Problem hierbei ist, dass die Klassenbibliotheken mit einem FullPath-Dateinamen eingetragen werden und sich somit immer auf die selben Dateien beziehen. Wer also normalerweise mit projektspezifischen Kopien seiner allgemeinen Bibliotheken arbeitet - und das machen die meisten Entwickler - hat hier viele Möglichkeiten für unnötige Fehler. Falls man immer wieder an verschiedenen Projekten arbeitet, müssen diese Einstellungen jeweils neu vorgenommen werden, was sich nur über ein externes/eigenes Tool schnell und sicher erledigen läßt, einschließlich der Möglichkeit relative Pfade zu benutzen.

Neben diversen frei verfügbaren komfortablen Tools (u.a. auf unserer Homepage), die das projektspezifische Setzen der relevanten Registry-Einträge ermöglichen, kann man natürlich auch REGEDIT dafür einsetzen. Hierzu öffnet man in REGEDIT den entsprechenden Zweig und schreibt ihn mittels "Exportieren" in eine *.REG-Datei. Diese Datei kann man dann mit einem Editor bearbeiten und aus den absoluten dann relative Pfade machen. Hier ist zu beachten, dass die Standard REG-Dateien unter Windows2000 keine Textdateien mehr sind, es muss daher ein Export als Typ REGEDIT 4 erfolgen.

Folgender Code reicht dann zum Setzen der Einstellungen aus:

    if file("OPTIONS.REG")
       run /n regedit /s options.reg
    endif

Der Parameter /s verhindert dabei das Anzeigen einer Sicherheitsabfrage.

Das Befehlsfenster

Das Befehlsfenster ist eines der Features, durch das sich VFP von den meisten anderen Entwicklungsumgebungen abhebt. Neben der Möglichkeit Code einzugeben und ihn sofort interaktiv zu testen, was sich insbesondere beim Testen komplexer Befehle, wie z.B. von geschachtelten Stringoperationen sehr zeit- und nervensparend auswirken kann, hat das Befehlsfenster ein „Gedächtnis“. Dieses Gedächtnis ist die Liste der zuletzt ausgeführten Befehle, die nahezu beliebig lang werden kann (weshalb Microsoft wohl auch die Option „Clear“ im Kontext-Menü des Fensters untergebracht hat). Dass auch Microsoft diese „Gedächtnis“-Funktion erkannt hat, lässt sich u.a. an der intensiven Nutzung durch Visual Sourcesafe 6.0 ablesen, das seine Kommentare beim Ein-/Auschecken von Dateien als Kommentarzeilen in das Befehlsfenster schreibt. Auf diese Weise kann man sich später jederzeit ohne Aufwand ansehen, was Sourcesafe denn so alles angestellt hat.

Diese „Gedächtnis“-Funktionalität kann von eigenen ProgrammeGGIFť2Ibr>

    KEYBOARD “{CTRL-F2}{CTRL-END}*Mein Code{Enter}”

positioniert den Cursor in der letzte Zeile des Befehlsfensters (vorausgesetzt das Standard „Window“-Menü ist nicht ausgeblendet), fügt dort den Text „*Mein Code“ ein und schließt die Zeile mittels der Enter-Taste ab. Durch den „*“ handelt es sich um eine Kommentarzeile, die keine weitere Aktion ausführt. Hier kann natürlich auch beliebiger Code stehen, der zu einem beliebigen Zeitpunkt durch das Entfernen des „*“ ausgeführt werden kann.

Ein weiterer Grund, das Befehlsfenster in dieser Form einzusetzen, ist die Eigenart von VFP, beim Ausführen eines MODIFY FILE oder MODIFY COMMAND aus einem Programm heraus, diverse Menüfunktionen zu deaktivieren, u.a. das Aktivieren des Befehlsfenster über das „Window“-Menü ({CTRL-F2}). Um dies zu verhindern, kann man den entsprechenden Befehl ebenfalls per KEYBOARD über das Befehlsfenster ausführen lassen und kann dann wie gewohnt beliebig zwischen allen Fenstern wechseln.

Beim nächsten Mal weiter mit...

Auf die vorhergehend beschriebene Weise lassen sich u.a. auch recht einfach mehrere Befehle vorbereiten, die in einem bestimmten Zusammenhang gemeinsam benötigt werden, z.B. MODIFY-Befehle für die Klassen/Komponenten, an denen gerade gearbeitet wird und an denen am nächsten Tag weitergearbeitet werden soll:

    *do (_Browser) with "lea\abizobj"
    *modi form forms\aufwandserfassung
    *modi class Cntzeitaufwand of lea\abizobj
    *modi class Cntzeitaufwanddatum of lea\abizobj.

Zur Vereinfachung lässt sich mit dem beiliegenden STUFKEYB.PRG eine beliebige ASCII-Datei in das Befehlsfenster übertragen. Erfolgt dieses Vorbelegen des Befehlsfenster automatisch beim Start von VFP, so kann man sofort da weitermachen, wo man am letzten Arbeitstag aufgehört oder beim letzten GPF hängen geblieben ist.

Abkürzungen für den Codier-/Testzyklus

Das Codieren neuer Funktionalitäten (Prozeduren, Klassen...) besteht in der Regel aus einem mehr oder weniger oft durchlaufenen Zyklus aus Codieren - Testen - Fehlersuche - Codieren ...

Das Auffinden und Öffnen des Codebereiches, an dem Änderungen vorzunehmen sind, wird von den meisten Entwicklern mit Hilfe der Maus erledigt. Diese „Maus-Schubser“ benutzen dabei zumeist den Projektmanager oder den Classbrowser um eine Klasse zu öffnen, die nochmals überarbeitet werden muss (oder bei einem Programmabbruch die „Fix“-Funktion des Debuggers).

VFP unterstützt hierbei den Entwickler durch das automatische Öffnen des zuletzt geöffneten Codefensters. Falls man diese Funktionalität nutzen möchte, sollte man dieses Fenster daher tunlichst nicht vor dem Speichern schließen. Als komfortabler, immer sofort funktionierender  und vor allem schneller Ersatz für die diversen nötigen Mausklicks bietet sich die Verwendung des MODIFY CLASS/FORM-Befehls an, insbesondere dessen Klausel METHOD:

    MODIFY CLASS <MeineKlasse> OF <MeineClassLib> METHOD <MeineMethode>

Einmal im Befehlsfenster eingegeben, kann dann somit beliebig oft angewendet werden – wäre er nur nicht so umständlich einzugeben...

MODI CLASS SYS(1270)

Für bereits existierende Klassen läßt sich diese Tipparbeit stark vereinfachen. Mit Hilfe der Funktion SYS(1270) kann man sich eine Referenz auf das Objekt unter dem Mauszeiger holen. Über die gelieferte Objektreferenz kommt man recht einfach an den Namen der Klasse und der Klassenbibliothek. Auf diese Weise kann man über ein kleines per Hotkey ausgeführtes Programm (im beiliegende Menü enthalten)

    local oj, lcClasslib
    oj = sys(1270)
    if type("oj")#"O"
       wait window "Es befindet sich kein Objekt unter dem Mauszeiger"
       return
    endif
    * Wenn die Klasse gerade im Class Designer geladen ist
    * liefert .classlibrary die ClassLib der *PARENT*klasse...
    lcClasslib = oj.classlibrary
    if type("oj.parent.caption")="C" and oj.parent.caption = oj.name
       lcClass = "("+ upper(oj.name) +")"
       if lcClass $ wontop()
          lcClasslib= substr( wontop(), at("-", wontop() )+2 )
          lcClasslib = alltrim(strtran( lcClasslib, lcClass, ""))
       endif
    endif

den kompletten Befehl zum Bearbeiten der entsprechenden Klasse in das Befehlsfenster schreiben:

    keyb "{ctrl+f2}{ctrl-end}modi class ";
    + oj.class + " of " + lcClassLib
    oj=.null.

Das ganze funktioniert auch in einer vollgepackten Maske mit geschachtelten Containern, man muss nur mit dem Mauszeiger an das gewünschte Objekt herankommen.

Routinetätigkeiten

Die (mal eben eine schnelle-) Sicherungskopie

Vorab: Die Bereitschaft zum Anfertigen von Sicherungskopien, die im privaten Verantwortungsbereich des Entwicklers liegen, also nicht die automatischen Bandsicherungen im Netz, ist stark davon abhängig, wie unkompliziert, schnell und sicher der Vorgang erledigt werden kann.

Selbst für die Anwender von Visual SourceSafe kommt es immer wieder vor, dass man vor oder nach einer Änderung, oder auf den Anruf eines Mit-Entwicklers: "schick mir mal deine aktuellen Files per Mail", seine eigenen Sourcen und/oder Daten sichern möchte. Solche Sicherungen sind recht einfach in ZIP-Files unterzubringen, wobei mehrere historische Versionen gespeichert werden können, wie z.B. 981025.ZIP, 981101.ZIP usw. Zum Sichern wird hierfür zumeist WinZip eingesetzt. Winzip hat eine gut bedienbare Oberfläche, die leider nur eingeschränkt für das wiederholte Sichern auszuwählender Dateien geeignet ist, da hierfür, je nach Anzahl der Dateien, viel Zeit und Sorgfalt notwendig ist („Oh – die VCX hätte noch dabei sein müssen...“).

Mit einer kleinen VFP-Oberfläche und einen Freeware-ZIPer („Info-Zip“, Basisbibliothek u.a. für WinZip, http://quest.jpl.nasa.gov/Info-ZIP), der lange Dateinamen beherrscht, lässt sich diese Arbeit schnell und sicher erledigen:


 

Über eine List-Datei lässt sich hier vorgeben, welche Dateien gesichert werden sollen. Alternativ können alle Daten- oder Programmdateien gesichert werden.

Standardisierte Abläufe erzwingen

Viele Flüchtigkeitsfehler lassen sich durch minimalen Aufwand sicher ausschließen, indem die zu erledigende Aufgabe von einem Programm ausgeführt wird. Hierdurch können Vor- und Nachbedingungen geprüft werden und ggf. Aktionen automatisch ausgeführt werden. Im Folgenden sind einige Möglichkeiten aufgeführt.

Projekte öffnen

Durch die größtenteils vorhandene Kompatibilität zwischen VFP 5,6 +7  kann es sehr leicht passieren, dass ein Entwickler ein Projekt mit der falschen Version bearbeitet. Das kann, da die beiden Versionen nicht 100%ig kompatibel sind, später zu Problemen führen, falls das kompilierte Programm unter der jeweils anderen Version ausgeführt wird. Benutzt man z.B. die Systemvariable _SAMPLES, kompiliert das Programm unter VFP 5 und führt es dann unter VFP6 aus, dann wird der Fehler "Variable _SAMPLES not found" ausgegeben. Im Befehlsfenster ist ? _SAMPLES aber verfügbar...

Auch hier kann recht einfach eine Sperre eingebaut werden, indem das _Startup Programm zunächst eine entsprechende Prüfung im Projektverzeichnis vornimmt, z.B. über das Auslesen der zu benutzenden Version aus einer INI-Datei.

xCase-Integration

Views testen

Für das Erstellen und vor allem das Pflegen komplexer Datenmodelle insbesondere der Views sollte man möglichst xCase einsetzen, u.a. da der VFP eigene View Designer einige Bugs hat. Da das Erstellen von Views – zumindest bei mir – oft ein inkrementelles Annähern an die letztendlich richtige Version ist, ist es sehr zeitraubend, das geänderte Datenmodell nach VFP zu übertragen um dann dort die neue View auszuprobieren. xCase bietet die Möglichkeit den kompletten SQL-Befehl anzuzeigen. Dieser ist leider so umgebrochen, dass er in VFP nicht ausgeführt werden kann. Der simple Befehl

    _cliptext = strtran(_cliptext, chr(13), ";"+chr(13))

wandelt den zuvor in das Clipboard übertragenen SQL-Befehl in eine im Befehlsfenster sofort ausführbare Form um.

DBC aktualisieren

Beim Aktualisieren eines DBCs hat xCase die unangenehme Eigenschaft, bereits vorhandene Views, die in xCase entfernt wurden, nicht aus der Datenbank zu entfernen. Der folgende Codeblock löst das Problem:

    ? "Views aus DBC entfernen...  Anzahl: "
    FOR i=1 TO aDBObjects(laViews,"VIEW")
      DELETE VIEW (laViews[i])
    ENDFOR
    ?? i-1

Ein weiterer "beliebter" Fehler ist die Benutzung von Stored Procedures, die eigentlich zu einem anderen Modell gehören. Da in den entsprechenden xCase Dialogen ein FullPath angegeben wird, sollte man diesen tunlichst prüfen, z.B. indem man folgenden Code automatisch im Modell-Verzeichnis ausführt:

    lcfile = forcepath( "ddcod.dbf" ,trim(directory))
    use &lcFile
    ? "Lage der Stored Procedures / RI-Code kontrollieren:"
    list trim(title),script off

Auch nebenbei noch schnell GENDBC auftzurufen um das erzeugte PRG als Basis für einen Versionsvergleich in SourceSafe zu benutzen hat sichbewährt

Variable Art der Primärschlüssel

In xCase wir die Art und die Benennung der Primärschlüsselfelder durch einen Wert in der Datei XCASE.INI gesteuert. Arbeitet man an mehreren Projekten, in denen diese Feldtypen unterschiedlich sind, wie z.B. in einem Projekt CID c(10) und in einem anderen IID I, so sollte man möglichst jeweils ausschließlich die richtigen Typen benutzen. Wenn xCase grundsätzlich über das Entwicklermenü gestartet wird, so kann dort eine Abprüfung erfolgen, ob im Model-Verzeichnis( <xCasePfad> + „\“ + <DBC-Name> ) ein bestimmtes Programm vorhanden ist, das dann ggf. folgenden Code ausführt:

    = WritePrivateProfileString( "numerators", "vfp", ;
             "IID,i,4,0,i%sID", lcxCasePath + "xCase.INI")

Abbildung 7: xCase-Integration

Andere Utilities können prüfen, ob selbstdefinierte Erweiterungen im Datadictionary enthalten sind, auf Wunsch noch schnell eine Sicherungskopie erstellen, einen Dump in eine Textdatei erstellen...

Pfade die Erste: EXE erzeugen + Datenumgebung

Beim Erzeugen einer EXE-Datei sind Pfade, die automatisch in der erzeugten Applikation landen, eine beliebte Fehlerquelle. Um diese auszuschließen kann entweder ein Programm benutzt werden, das die notwendigen Prüfungen vornimmt und dann ein

    BUILD EXE FROM <MeinProjekt>

ausführt, oder man benutzt einen der Projektmanager-Hooks, indem man dem Projekt eine Klasse zuweist, die dann die verschiedenen Hooks abhandelt (z.B. das BeforeBuild-Event).

VFP hat die unangenehme Eigenschaft die Pfade der beteiligten Dateien in die erstellten EXE-Dateien einzubinden. Ein beliebtes Problem ist das Erzeugen der EXE auf Laufwerk D, welche dann auf fast allen Rechnern anstandslos läuft, außer auf Rechnern mit einer Fesplatte, und einem leeren CD-Laufwerk mit dem Laufwerksbuchstaben D. Hier kann der Entwickler dann gewarnt werden, oder die EXE mit einem entsprechenden Programm (z.B. Datei DEVAPPRD.ZIP aus dem DFPUG-Forum in Compuserve) direkt gepatcht werden.

Vorsicht Datenumgebung

Falls Sie Masken mit Datenumgebungen benutzen und die erstellte EXE auf dem selben Rechner, aber in einem anderen Verzeichnis oder Laufwerk (=Kundensytem-Simulation) mit den dort befindlichen Daten getestet werden soll,  so ist Vorsicht angesagt. VFP ist so "clever", dass es ggf. die Datenbank im Entwicklungspfad findet und dann auch öffnet. Was dieses "Feature" auf einem Kundensystem anrichten kann, auf dem z.B. ein Entwickler zu Testzwecken zusätzlich eine lokale Installation vorgenommen hat, kann sich wohl jeder vorstellen.

Abhilfe:

  • keine oder nur programmatische Datenumgebung benutzen

  • Datenumgebung zwar benutzen aber Tabellen im Load programmatisch selbst öffnen

  • Pfade in EXE/APP patchen (pathpatch.prg auf der Begleit-CD)

  • Prinzip Hoffnung anwenden

Kammerjäger

Dieser Abschnitt zeigt wie man das Testen und Debuggen der eigenen Programme einfacher und effizienter gestalten kann.

Testcode? - Testcode!

Je mehr ein Entwickler dahin kommt, seinen Klassen eine genau umrissene Aufgabe zu geben und sie gut zu kapseln, um so leichter wird es für Ihn, diese auch zu testen. Zum Testen sollte eine Klasse normalerweise so angesteuert werden, wie das auch in der laufenden Applikation geschehen soll. Diese Tests kann man in einer laufenden Applikation durchführen, was jedoch in der Regel relativ aufwendig ist, da man in der Applikation ja für jeden Testlauf zuerst an die Stelle gelangen muß, an der die zu testende Klasse ins Spiel kommt. Falls irgendwie möglich, sollte man deshalb statt dessen ein Programm benutzen, das die nötige Umgebung einstellt und dann die einzelnen Funktionen der Klasse gezielt auf Korrektheit überprüft. Diese Vorgehensweise hat mehrere Vorteile:

  • der Testcode enthält vollständige Anwendungsbeispiele

  • der Entwickler wird angeregt sich Gedanken dazu machen, was seine Klasse machen soll und was nicht

  • Die Umgebungsvoraussetzungen für die Klasse werden klarer

  • der Test lässt sich einfach und schnell wiederholen (s.a. Abkürzungen für den Codier-/Testzyklus )

Um den Code für zukünftige Tests wieder nutzen zu können, z.B. weil die Klasse plötzlich einen Fehler hervorruft, kann er, damit er nicht verloren geht, direkt in die Klasse eingebunden werden. Es bietet sich eine eigene Methode „_TESTCODE“ an, die bereits in den Basisklassen definiert wird. Im Bedarfsfall wird hier einfach der Testcode aus dem PRG eingesetzt:

    PROCEDURE _TestCode
    #IF .F.
    dazwischen den Code zum Testen der Klasse / wird nicht mit eincompiliert!
    #ENDIF

Der Testcode sollte Rückgabewerte und ggf. Meldungen ausgeben, aus denen eindeutig und leicht nachvollziehbar hervorgeht, ob der Test funktioniert hat.Sobald Wissen über Zusammenhänge in den angezeigten Werten notwendig ist, wird die Fehlersuche erschwert, da der Tester sich ggf. zunächst einarbeiten muß um die Ergebnisse zu interpretieren. Interessant ist ja zunächst nur die Aussage „Funktioniert“ oder „Funktioniert nicht“.

Testcode kann wie folgt aussehen:

    * Vorlage für Testcode
    set class to devcon addi
    o = crea("testcodebeispiel")
    clear
    * liefert alles rechts von “cd” aus “abcdefg”
    ? doit('o.cSubstr_Rechts_von("abcdefg", "cd")';
    , "uDidIt='efg'" , "'a'$uDidIt")
    ? doit('o.cSubstr_Rechts_von("abcdefg", "g")', "uDidIt=''")
    *********************************
    proc doit
    *!DEV Joachim Hilgers
       *
       *!E  ?doit('o.meth()', "eval1", "eval2",...,"eval9")

... ausführlich dokumentierter Sourcecode auf der Begleit-CD

Die Zeilen mit „? doit(...“  zeigen an, welcher Befehl jeweils ausgeführt wurde, dessen Rückgabewert und ermöglichen gleichzeitig die Abprüfung mehrere Bedingungen. Bei einem Fehler sieht die Bildschirmausgabe dann wie folgt aus:

    o.cSubstr_Rechts_von("abcdefg", "cd")Ergebnis: efg
    #### E-R-R-O-R: 'a'$uDidIt
    o.cSubstr_Rechts_von("abcdefg", "g")Ergebnis:

Es ist also sofort zu sehen, dass die Klasse den Test nicht bestanden hat.

Lebende Komponenten

Komponenten, die per Drag&Drop zu Containern, wie z.B. Masken hinzugefügt werden um diese um Funktionalitäten zu erweitern, werden in der Regel auf Visible=.F. gesetzt, und sind somit zur Laufzeit nicht sichtbar. Wenn Sie diese Komponenten so gestalten, dass sie eine Labelklasse sind oder ein Label enthalten, so kann dessen Caption sehr einfach genutzt werden, um dem Entwickler sofort Informationen über den Status der Komponente anzuzeigen. Wenn man diesen Komponenten dann noch Code im Click-Event spendiert, dann kann man damit beliebige Hilfestellungen für das Debuggen liefern.

Der Vorteil für den Entwickler ist hierbei der, dass er Statusinformationen erhält, ohne den Debugger starten zu müssen, was beim Entwickeln komplexer Applikationen viel Zeit sparen kann. Abgesehen davon, dass sich einzelne Applikationen mit aktiviertem Debugger anders verhalten als ohne...

Beispiele für entsprechende Komponenten sind:

    Ein Debug-Modus-Setzer, der ein applikationsweites „im Debug-Modus J/N“ Flag setzt, kann „ON/OFF“ anzeigen sowie seine Farbe ändern

    Eine Login-Komponente kann den Namen und/oder die interne ID des gerade angemeldeten Benutzers anzeigen

    Ein Connector kann durch seine Farbe und seine Caption anzeigen ob und mit welchem Objekt er verbunden ist

    Ein Message-Interface kann den Titel der letzten Nachricht anzeigen

    Eine Komponente, die einen länger dauernden Prozess abarbeitet, kann zuerst ihre Farbe in grün ändern (=arbeitet) und nach dem Beenden wieder auf den Standardwert

    eine SQL-ShowPlan-Komponente kann per Klick den Status von SYS(3054) ändern und somit in der laufenden Applikation den Grad der Rushmore- Optimierung anzeigen

Diese Komponenten könnten ihre Visible-Eigenschaft natürlich automatisch in Abhängigkeit vom Debug-Modus Flag setzen, so dass im Debug-Modus plötzlich viele "lebende Komponenten" Ihre Masken bevölkern...

VCX-Pflügen

VFP-Klassenbibliotheken (Endungen VCX u. VCT) sind ganz normale DBF-Tabellen, die recht einfach nach in ihnen enthaltenen Informationen durchsucht werden können.

Pfade die zweite: "externe" Bibliotheken

Um zu kontrollieren, ob und welche Pfade in den beteiligten Klassenbibliotheken enthalten sind (s.a. Pfade die Erste: EXE erzeugen + Datenumgebung) reicht es aus, die entsprechende(n) VCX(e) per USE MeineVCX  zu öffnen. An alle enthaltenen Pfade kommt man dann recht einfach heran mit 

    scan for "\" $ classloc+reserved8

Auf der Begleit-CD befindet sich das Programm CLSPATH, das alle SCX und VCX-Dateien eines Verzeichnisbaumes durchforstet und die gefundenen Pfade in einer Browse anzeigt. Auf diese Weise hat man sehr schnell den Überblick, ob ein „Ausreißer“ dabei ist.

Versionsnummern und zuständige Entwickler

Falls man sich auf Namen der Properties für den für die einzelnen Klassen zuständigen Entwickler und die Versionsnummer festgelegt hat, wie z.B. _version und _entwickler, so ist es ein Leichtes, sich eine Liste der Klassen eines Entwicklers mit deren Versionen ausgeben zu lassen. Die Frage „welche Versionen hast du denn“ lässt sich dann sofort beantworten (s.a. Programm CLSVER auf der Begleit-CD).

View-Debugger

Parameterisierte Views zu testen ist nicht immer ganz einfach, oder mindestens ziemlich umständlich. Die Parameter verweisen zumeist auf die Properties eines Objektes. Zum Testen muss dieses Objekt also vorhanden sein. (M)ein View Debugger ist eine Komponente, die in der Einsatzumgebung der zu testenden View, z.B. auf einer Form eingesetzt wird. Nach Einstellung des Namens der zu testenden View, entnimmt die Komponente per

    dbgetprop(tcViewName, "view", "sql")

den SQL-String aus dem Datenbankcontainer und setzt die zu diesem Zeitpunkt im Zugriff befindlichen Werte der Parameter als Konstanten in den String ein. Dieser ist dann zum einen sehr einfach lesbar und kann, ggf. nach manuellen Veränderungen der Werte, beliebig oft außerhalb der Applikation ausgeführt und getestet werden.

Der View-Debugger befindet sich als Komponente in der Klassenbibliothek D-OPT auf der Begleit-CD

Auto Code Destroy / mehrere VFP-Instanzen

Während der Entwicklung einer Applikation muss in der Regel häufig zwischen "Entwickeln" und "Applikation Testen" gewechselt werden. Auch hier gibt es ein VFP-"Feature" zu beachten. Unter bestimmten (und leider nicht immer reproduzierbaren) Umständen hält VFP anscheinend die eben beim Testen benutzten Klassenbibliotheken im Speicher und bietet dem Entwickler leere Kopien der Bibliotheken zum Bearbeiten an. In diesen Kopien befindet sich keinerlei Quellcode mehr, zumindest jedoch keiner, der vom Editor angezeigt werden könnte. Wenn der Entwickler in einer solchen Kopie irgendwelche Änderungen vornimmt ("oops-da habe ich meine letzte Änderung wohl nicht gespeichert...") und diese dann speichert, dann befindet sich in der neuen Version nur noch dieser Quellcode.

Diesem Problem kann man auf zwei Wegen begegnen. Und zwar indem man zunächst immer prüft, ob noch anderer Code enthalten ist oder indem man mit einer zweiten VFP-Instanz arbeitet. Folgender Code führt das komfortabel aus dem Entwicklermenü heraus über einen Hotkey (bei mir ALT-2) aus:

    local lcExe, lcConfig
    lcExe = iif( "FoxPro 06" $ version(), "VFP6.EXE", "VFP.EXE")
    lcexe = home() + lcExe
    * die selbe Config-Datei benutzen:
    lcConfig = iif( empty(sys(2019)), "", "-c"+sys(2019) )
    run /n &lcExe &lcConfig

Danach benutzt man einfach eine Instanz zum Testen und die andere zum Code-Wühlen. Ändert man dabei auch gleich die Caption der Test-Instanz mittels _screen.caption="TEST", dann gibt es auch keine Probleme mehr mit dem Verwechseln in der Taskbar.

Multiple FOXUSER.DBF

Ein kleines Problem ergibt sich dann allerdings mit der FOXUSER.DBF, die grundsätzlich exclusive geöffnet wird. Man könnte hier nach dem Prinzip "der erste gewinnt" verfahren, mir erscheint der Ansatz mit der Verwendung mehrerer Resource-Dateien jedoch sinnvoller.

Das kann z.B. so erfolgen, dass versucht wird, die Standard-FOXUSER.DBF zu öffnen. Falls das nicht klappt wird eine entsprechende Datei mit einer angehängten Laufnummern (FOXUSER01.DBF, FOXUSER02.DBF...) gesucht und ggf. automatisch erzeugt sowie benutzt.

    ...
    set reso on  && versuchen zu benutzen...
    if llok && kein fehler
       ? "Benutzte Resource-Datei:", SET("RESO",1)
    exit
    else
       * gibt es eine Foxuser dieser Laufnummer?
       lcNr = lcNr +1
       lcFile = getenv("temp") + "\FOXUSER" + trans(lcNr) + "." ;
                + justext(lcfile)
    if !file(lcFile)
          create table (lcFile);
                                   ...

Der vollständige Code hierfür befindet sich auf der Begleit-CD (OpenResource.PRG)

Sonstiges

Builder

Einer der Hauptgründe für den Einsatz von OOP-Techniken war und ist die Wiederverwendbarkeit. Wiederverwendbare Klassen - hierzugehören auch die „Komponenten“ – werden oft erst dadurch wiederverwertbar, dass sie über Einstellmöglichkeiten verfügen, die sie an die jeweilige Einsatzumgebung anpassen.. Einstellungen, die über Properties vorgenommen werden, lassen sich äußerst effektiv vereinfachen. Dies trifft um so mehr zu, je mehr eigendefinierte Properties eine Klasse enthält. Hier ist es für den anwendenden Entwickler, sowie nach einer gewissen Zeit für den, der die Klasse entwickelt hat, oft recht mühsam die Properties zu finden, an denen „gedreht“ werden muß. Hier bietet sich der Einsatz spezialisierter Builder an.

Als Basis für entsprechende eigene Builder bietet sich die Public Domain Klassenbibliothek BuilderB von Ken Levy  (www.classx.com) an. Hiermit läßt sich in wenigen Minuten eine Eingabemaske zum Einstellen der gewünschten Properties erstellen, die dann jederzeit beim Bearbeiten einer Klasse über das Kontextmenü und den Punkt „Builder“ aufgerufen werden kann.

Die Schritte zum Aufbau eines solchen BuilderB-Builders sind recht einfach:

  • Eine neue Klasse basierend auf Builderform aus BuilderB.vcx erstellen. Die Klasse kann in der selben VCX wie die zu einzustellende Klasse gespeichert werden.

  • BuilderB.vcx in die „FormControls“-Toolbar aufnehmen (über Button „View Classes“ - „Add“)

  • Entsprechend dem Datentyp der zu bearbeitenden Properties  die Klassen „BuilderEditbox“, „BuilderTextbox“ oder „BuilderCheckbox“ auf die Form fallen lassen

  • Für jedes Control über das Kontextmenü den hierfür vorgesehenen und mitgelieferten Builder aufrufen und  den Property-Namen, den Statusbartext und ggf. eine Valid-Klausel eingeben

  • In der Klasse, für den der Builder benutzt werden soll, ggf. ein neues Property „Builder“ erzeugen und den Namen des Builder eintragen. Falls der Builder in einer anderen VCX gespeichert wurde, muß der Name der  Builder-Klassenbibliothek, gefolgt von einem Komma, dem Namen der  Builderklasse vorangestellt werden.

Fertig!

vorheriger Vortrag D-BUILD

zur Übersicht der Gruppe PROG

nächster Vortrag D-HELP

 

dFPUG c/o ISYS GmbH

Frankfurter Str. 21 b

 

D-61476 Kronberg

per Fax an:

+49-6173-950903

oder per e-Mail an:

konferenz@dfpug.de

© Texte, Grafiken und Inhalt: ISYS GmbH