Zum Hauptinhalt springen

FAQ - Programmierung

FAQ - Programmierung Häufige Fragen zur Programmierung

Wofür stehen die verschiedenen Abkürzungen bei den Befehlsnamen?

Alle Befehle sind über verschiedene Präfixe aufgebaut. Dabei stehen die Abkürzungen für verschiedene Befehlsbereiche. Ebenso werden Eigenschaften in verschiedene Klassen (Oberflächen-, Druck-, System-Eigenschaften usw.) eingeteilt.

  • Abkürzung: Bedeutung
  • Bin: Binary Object (Binäres Objekt, BLOb)
  • Cell: GanttGraph Cell (Zelle eines GanttGraph-Objektes)
  • Clm: Column (Eigenschaften von Spalten-Objekten)
  • Cnv: Convert (Konvertierungsbefehle)
  • Col: Color (Farbeigenschaften und -konstanten)
  • Com: Component Object Model (Befehle und Eigenschaften der COM-Schnittstelle)
  • Cte: Container Element (Dynamische Strukturen)
  • Ctx: Container Element Extended (Oberflächenobjekt, welches COM-Objekte externer Applikationen, wie beispielsweise ein Word-Dokument, darstellen kann)
  • Date: Date (Datumseigenschaften und -befehle)
  • Dba: Database (Datenbank-Befehle)
  • Db: Database (Eigenschaften der Datenstruktur)
  • Dbg: Debugger (Befehle und Konstanten des externen Debuggers)
  • Dde: Dynamic Data Exchange (Befehle der DDE-Schnittstelle)
  • Dll: Dynamic Link Library (Befehle zum dynamischen Einbinden von Bibliotheken)
  • Dta: Data Transaction (Transaktionsbefehle)
  • Err: Error (Fehlerbehandlung)
  • Evt: Event (Ereignis)
  • File: File (Datei / Tabelle)
  • Fld: Field (Datensatzfeld-Befehle)
  • Flt: Filter (Filter-Befehle)
  • Fmt: Format (Formatierungskonstanten und -eigenschaften)
  • Fsi: File System Interface (Befehle für externe Dateien)
  • Hdl: Handle (Deskriptor-Befehle)
  • Key: Key (Schlüssel / Index)
  • Lcl: Locale (Länderspezifische Einstellungen)
  • Link: Link (Verknüpfung)
  • Mail: E-Mail-Befehle
  • Msx: Message-Exchange-Befehle
  • Nti: Network information (Informationen über das Netzwerk)
  • pp: Print Property (Druck-Eigenschaft)
  • Ppv: Print Preview (Objekte der Druckvorschau)
  • Prt: Printer (Drucker oder Druck-Job)
  • _PrtProp: Print Property (Druck-Eigenschaft)
  • _r: Fehlerkonstanten der Datensatz-Befehle
  • Rec: Record (Datensatz-Befehl)
  • RecBuf: Record-Buffer (Datensatzpuffer)
  • Rtf: Rich Text Format (Objekte und Befehle der Rtf-Objekte)
  • Sbr: Subrecord (Teildatensatz)
  • Sck: Socket (Befehle und Konstanten der Sockel-Befehle)
  • Sel: Selection (Befehle und Konstanten der Selektionen)
  • sp: System Property (System-Eigenschaft)
  • Sto: Storage (Befehle und Konstanten für Storageobjekte)
  • Str: String (Befehle und Konstanten für Zeichenkettenbefehle)
  • Sys: System (System-Befehl)
  • _SysProp: System Property (System-Eigenschaft)
  • Tapi: Telephon Application Programming Interface (Telefon-Schnittstelle)
  • Text: Text (Befehle und Konstanten zur Bearbeitung interner Texte)
  • Type: Type Information (Information über Feld- und Variablentypen)
  • Urm: User and Rights Management (Benutzer- und Rechte-Verwaltung)
  • User: User (Benutzerinformationen)
  • vm: Variable method (Methoden von Variablentypen)
  • vp: Variable property (Eigenschaften von Variablentypen)
  • Win: Windows (Oberflächen)
  • WinMsd: Multi Selection Data (Mehrfachselektion)
  • _WinProp: Windows Property (Oberflächen-Eigenschaft)
  • wp: Windows Property (Oberflächen-Eigenschaft)
Was ist bei der Verwendung eines RtfEdit-Objekts in einem Notizbuch zu beachten?

Die Verwendung der Tastenkombination Strg + Tab führt bei dem RtfEdit-Objekt zum Anspringen des nächsten Tabulators. Befindet sich das RtfEdit -Objekt auf einer Notizbuchseite findet statt dessen ein Wechsel der Notizbuchseite statt.

Die gleiche Problematik ist auch vorhanden, wenn beim RtfEdit -Objekt in der Eigenschaft RtfEditFlags die Ausprägung _WinRtfEditTabSingle gesetzt ist. Die Tastenkombination Strg + Tab soll dann zum Fokuswechsel aus dem RtfEdit -Objekt führen, es findet aber ein Wechsel der Notizbuchseite statt.

Das Anspringen des nächsten Tabulators oder der Fokuswechsel kann durch das Setzen der Eigenschaft Flags (des Notebook -Objektes) auf den Wert _WinFlagNbCtrlTabOff erreicht werden.

Beispiel:

sub EvtPageSelect
(
aEvt : event; // Ereignis
aPage : int; // Notebook-Seite
aSelecting : logic; // Seite aktiviert / deaktiviert
) : logic;
{
if (aSelecting)
{
if (aPage->wpName = 'nbPageRtf')
// Strg+Tab führt nicht zum Wechsel der Notizbuchseite
$Notebook->wpFlags # $Notebook->wpFlags | _WinFlagNbCtrlTabOff;
else
// Strg+Tab führt zum Wechsel der Notizbuchseite
$Notebook->wpFlags # $Notebook->wpFlags & ~_WinFlagNbCtrlTabOff;
}

return(true);
}
Wie findet prinzipiell die Verarbeitung von Ereignissen statt?

In der Verarbeitung von Dialogen sind in CONZEPT 16 drei unterschiedliche Ebenen zu betrachten. Das Betriebssystem, das CONZEPT 16-System und die Applikationsoberfläche.

Wird eine CONZEPT 16-Applikation gestartet, wird in der Regel sofort ein Dialog geöffnet. Innerhalb von CONZEPT 16 wird zu diesem Zeitpunkt keine Prozedur mehr verarbeitet. Alle weiteren Aufrufe von Funktionen werden von "Außen" angeregt.

Von "Außen" angeregt bedeutet, dass Ereignisse entweder durch das System oder den Benutzer ausgelöst werden. Damit solche Ereignisse verarbeitet werden können, läuft unter Windows eine Endlos-Schleife. Diese Ereignisschleife hat nur die Aufgabe Ereignisse entgegen zu nehmen und die Weiterverarbeitung anzustoßen.

Wird durch den Benutzer zum Beispiel mit dem Mauszeiger auf eine Schaltfläche geklickt, wird eine ganze Reihe von Tätigkeiten durchgeführt. Zunächst wird das Ereignis durch die Ereignisschleife des Betriebssystems festgestellt und in eine Warteschlange einsortiert. Die Ereignisse in dieser Warteschlange werden nacheinander abgearbeitet. Vom Betriebssystem wird der Typ des Ereignisses ermittelt. Einige Ereignisse werden vom Betriebssystem selbst verarbeitet (zum Beispiel das Verschieben eines Fensters, das Bewegen des Mauszeigers u.ä.), andere werden an das entsprechende Programm weitergereicht. In unserem Beispiel werden weitere Informationen gesammelt (welches Objekt angeklickt wurde) und das Ereignis an CONZEPT 16 weitergereicht.

Innerhalb von CONZEPT 16 wird geprüft, ob dem Ereignis eine Funktion zugeordnet wurde. Hat der Programmierer in dem Ereignis EvtClicked der Schaltfläche eine Funktion angegeben, wird diese aufgerufen. Nach dem Abarbeiten der Funktion wird dessen Ergebnis von CONZEPT 16 an das Betriebssystem weitergegeben. Im Falle von EvtClicked hat das Ergebnis keine Auswirkungen.

Das Ereignis ist damit abgearbeitet und das Betriebssystem kann das nächste Ereignis verarbeiten oder auf das nächste Ereignis warten.

Beim Testen des Dialogs in der Entwicklungsumgebung hat das Drücken von Schaltflächen und das Aufrufen von Menüeinträgen keine Wirkung.

In der Entwicklungsumgebung gibt es zwei Möglichkeiten einen Dialog zu testen. Über den Menüpunkt Datei / Test kann nur der Dialog getestet werden. Es werden keine Ereignisfunktionen aufgerufen. Damit bleibt das Drücken von Schaltflächen und das Aufrufen von Menüeinträgen ohne Funktion.

Im Editor kann über den Menüpunkt Prozedur / Ausführen eine Prozedur gestartet werden. Zeigt diese Prozedur einen Dialog an, werden auch alle Ereignisfunktionen, die über diesen Dialog aufgerufen werden, durchgeführt. Der Dialog kann dann auch in seiner Funktion getestet werden.

Wie kann nach dem Starten des Clients ein Bild als Hintergrund für die Applikation angezeigt werden?

Ab der CONZEPT 16-Version 4.4.05 kann ein Applikationsfenster mit einem Hintergrundbild versehen werden. Das Bild muss zuvor in die Datenbank importiert und kann dann über die Eigenschaft PictureName gesetzt werden.

Ab der Version 4.7.04 kann jedem Frame -Objekt ein Hintergrundbild zugeordnet werden. Dieses Bild kann sowohl in der Datenbank als auch als externe Datei vorliegen.

Wie können mehrdimensionale Arrays definiert werden?

Ab der CONZEPT 16-Version 4.2 können in A+-Prozeduren mehrdimensionale Arrays angelegt werden, wenn man eine einfache Funktion zur Berechnung eines skalaren Index verwendet.

Ein Vektor kann wie folgt deklariert werden:

local
{
vector : int[3];
}

Die Variable vector besteht aus drei Variablen vom Typ int. Das dritte Element dieses Arrays kann z. B. mit vector[ 3 ] angesprochen werden. Die Nummerierung startet bei 1.

Bei mehrdimensionalen Arrays wird die weitere Dimension mit Hilfe der Multiplikation in der Klammer angegeben:

local
{
arr2dim : int[4*3];
}

Hierdurch werden zwölf Variablen angelegt. Das Element (x,y) aus einem Array kann über folgende Formel angesprochen werden:

(y - 1) * xdim + x

wobei xdim die Anzahl der Elemente pro Zeile ist.

y \ x1234
11 ( ( 1 - 1 ) * 4 + 1 )2 ( ( 1 - 1 ) * 4 + 2 )3 ( ( 1 - 1 ) * 4 + 3 )4 ( ( 1 - 1 ) * 4 + 4 )
25 ( ( 2 - 1 ) * 4 + 1 )6 ( ( 2 - 1 ) * 4 + 2 )7 ( ( 2 - 1 ) * 4 + 3 )8 ( ( 2 - 1 ) * 4 + 4 )
39 ( ( 3 - 1 ) * 4 + 1 )10 ( ( 3 - 1 ) * 4 + 2 )11 ( ( 3 - 1 ) * 4 + 3 )12 ( ( 3 - 1 ) * 4 + 4 )

Auf diese Weise lassen sich beliebig-dimensionale Arrays erstellen.

Die Formel zur Berechnung der Position des Elements (x,y,z) in einem dreidimensionalen Array ermittelt sich aus

x + (y - 1) * xdim + (z - 1) * ydim * xdim

Mehrdimensionale Arrays können in der Auswertung verschiedener Daten verwendet werden. Beispielsweise können Umsatzzahlen nach Gebieten, Warengruppen und Monaten aufgeschlüsselt werden.

Wie kann das aktuelle Verzeichnis gewechselt werden?

In CONZEPT 16 kann mit den A+-Prozeduren das aktuelle Verzeichnis gewechselt werden. Dies geschieht mit dem Befehl FsiPathChange (). Das Wechseln des aktuellen Verzeichnisses ist bei der Ausführung durch den CONZEPT 16-Client verhältnismäßig unkritisch. Bei der Ausführung des Befehls durch den CONZEPT 16-Server, der externen Programmierschnittstelle oder der Web-Schnittstelle kann es allerdings zu Überschneidungen kommen, da mehrere Prozeduren in einer Prozess-Umgebung ablaufen. Das äußert sich derart, dass externe Dateien, die sich im aktuellen Pfad befinden sollen, nicht gefunden werden. Dies hängt mit dem Wechsel des aktuellen Verzeichnisses zusammen.

Das aktuelle Verzeichnis ist Bestandteil der Prozess-Umgebung. Laufen jetzt mehrere CONZEPT 16-Prozeduren in einer Prozess-Umgebung ab, z. B. mehrere Prozeduren werden auf dem CONZEPT 16-Server mit dem Befehl RmtCall () gestartet, kann die Prozess-Umgebung durch eine Prozedur verändert werden. D. h. eine Prozedur verändert die Umgebung für alle Prozeduren.

Die Veränderung des aktuellen Verzeichnisses sollte vermieden werden, gerade, wenn die Prozeduren vom Server, der externen Programmierschnittstelle oder der Web-Schnittstelle ausgeführt werden sollen. Alle Prozedurbefehle zum Öffnen (FsiOpen ()), Löschen (FsiDelete ()) oder Umbenennen (FsiRename ()) von Dateien bzw. zum Lesen von Verzeichnissen (FsiDirOpen ()) können einen vollständigen Pfad beinhalten, womit die Verwendung des aktuellen Verzeichnisses nicht notwendig ist.

Können aus A- Prozeduren Funktionen von A+ Prozeduren aufgerufen werden?

Auch aus A- Prozeduren können Funktionen von A+ Prozeduren aufgerufen werden. Dazu muss folgender Befehl angegeben werden:

Call('Lib.Rec:Insert', ...);

Der Call ()-Befehl ruft die Funktion Insert aus der Prozedur Lib.Rec auf.

Warum werden die Prozeduraufrufe von A- Prozeduren aus A+ Prozeduren mit Call() ignoriert?

Nach der Übersetzung liegen die Prozeduren in einem P-Code vor. Der P-Code unterscheidet sich je nach verwendetem Übersetzer. Beim Übersetzen von A- Prozeduren entsteht ein anderer P-Code, als beim Übersetzen von A+ Prozeduren.

Zur Interpretation zur Laufzeit werden daher auch zwei unterschiedliche Laufzeitumgebungen benötigt. Um den CONZEPT 16-Server, die Web-Schnittstelle und die Windows Programmierschnittstelle möglichst schlank zu halten, verfügen diese nur über die Laufzeitumgebung für A+ Prozeduren. A- Prozeduren können somit nicht interpretiert werden. Der Aufruf von diesen Prozeduren wird daher ignoriert.

Damit die entsprechenden Funktionalitäten ausgeführt werden können, müssen diese in A+ Prozeduren überführt werden. A+ Prozeduren können von A- Prozeduren aufgerufen werden (siehe "Können aus A- Prozeduren Funktionen von A+ Prozeduren aufgerufen werden?").

Beim Abfragen der Eigenschaft BiasMinutes kommt immer 0 zurück.

In der Eigenschaft vpBiasMinutes ist die Verschiebung der Uhrzeit durch die verwendete Zeitzone abgelegt. Die Eigenschaft gibt nur dann die korrekte Zeitzone zurück, wenn zuvor die Methode vmSystemTime () der Variablen aufgerufen wurde. Wurde die Methode nicht aufgerufen, wird 0 zurückgegeben.

Wozu werden Deskriptoren benötigt?

Deskriptoren bilden eine einfache Möglichkeit auf ein bestimmtes Objekt zuzugreifen. Bei dem Objekt kann es sich um ein Dialog-Objekt (Frame, Eingabe-Objekt, Menüeintrag usw.) oder um Puffer handeln. Mit Hilfe eines Deskriptors eines Dialog-Objektes können die Eigenschaften des Objektes manipuliert werden:

hdlObj->wpCaption # 'Name';

Der Deskriptor eines Objektes kann mit der Funktion $ und dem Namen des Objektes ermittelt werden. Bei der Verwendung von Ereignisfunktionen wird der Deskriptor des auslösenden Objekts bereits im Übergabeparameter aEvt :Obj übergeben.

Handelt es sich um ein Puffer-Objekt (zum Beispiel einen internen Text, einen Selektionspuffer oder einen Puffer einer externen Datei), kann der entsprechende Puffer manipuliert oder an einen Befehl übergeben werden.

// Ersten Datensatz aus einer Selektion lesen
RecRead(CST.F.Customer, hdlSel, _RecFirst);

Weitere Möglichkeiten ergeben sich aus der Beschreibung der entsprechenden Befehle.

Worin besteht der Unterschied zwischen dem Zugriff auf ein Dialog-Objekt über den Namen und seinem Deskriptor?

Bei der Verwendung der Funktion $ zusammen mit dem Namen des Objekts, wird der Objektbaum nach einem Objekt mit dem entsprechenden Namen durchsucht. Sobald der Name gefunden wurde, wird der Aufruf durch den Deskriptor des Objekts ersetzt.

Wird gleich der Deskriptor verwendet, entfällt die Suche. Die Funktion wird schneller abgearbeitet.

Wenn möglich sollte immmer über einen Deskriptor zugegriffen werden. In der Regel kann ein Eltern-Objekt schneller mit dem Befehl WinInfo (), als über seinen Namen ermittelt werden. Ebenso ist einer Schleife, welche die Deskriptoren aller Objekte eines übergeordneten Objektes ermittelt, gegenüber einer Auflistung aller Namen, den Vorzug zu geben.

Müssen bei einem Funktionsaufruf immer alle Parameter angegeben werden?

In A+ Prozeduren können Parameter von Funktionen als optionale Parameter angegeben werden. In der Deklaration des Funktionskopfes wird dem Parameter opt vorangestellt.

Beispiel:

sub Add4
(
aValue1 : int;
aValue2 : int;
opt aValue3 : int;
opt aValue4 : int;
) : int;
{
return(aValue1 + aValue2 + aValue3 + aValue4);
}

Die Funktion Add4() kann mit zwei, drei oder vier Parametern aufgerufen werden. Zurückgegeben wird die Summe der übergebenen Parameter. Parameter, die nicht angegeben werden, sind in der Funktion mit 0 initialisiert.

Für die Übergabe einer variablen Anzahl von Parametern können dynamische Strukturen verwendet werden. Die Parameter werden in einer Liste oder einem Baum gespeichert und die Wurzel der Struktur als Parameter an die Funktion übergeben. Innerhalb der Funktion müssen dann die Parameter aus der Struktur ermittelt werden.

In einer Mehrbenutzerumgebung soll eine Selektion mehrfach gestartet werden können.

Die Selektion wird vor dem Start kopiert (SelCopy ()). Der neue Name setzt sich aus einem Kürzel für die Selektion und der User-ID des Benutzers (UserInfo ()) zusammen.

// Name der Selektion erzeugen
tSelNew # 'TMP_SEL_' + UserInfo(_UserNumber);

// Selektion kopieren
SelCopy(Adr.F.Adresses, 'Customer', tSelNew);

// Selektion öffnen
tHdlSel # SelOpen();
tHdlSel->SelRead(Adr.F.Adresses, tSelNew, _SelLock);

// Selektion durchführen
tHdlSel->SelRun(aSelOptions);

// Selektion verarbeiten
...

tHdlSel->SelClose(); // Selektion schließen
SelDelete(tSelNew); // Selektion löschen
Die bei einer Selektion angehängte Prozedur wird nicht ausgeführt.

Bei der Definition einer Selektion kann hinter jeder Abfrage eine Prozedur angegeben werden, die nach Überprüfung der Abfrage durchgeführt wird.

Die dort angegebene Prozedur kann entweder eine 4.0 kompatible Prozedur (A-) oder eine A+ Prozedur sein. In Abhängigkeit von der Umgebung, in der die Selektion gestartet wird, kann aber nur eine A- oder eine A+ Prozedur durchgeführt werden, da die Rückgabe, ob ein Datensatz in die Selektionsmenge aufgenommen werden soll oder nicht, über unterschiedliche Rückgabemechanismen realisiert werden musste.

Dies hat zur Folge, dass eine angegebene A+ Prozedur nur dann durchgeführt wird, wenn die Selektion auch von einer A+ Prozedur (mit dem Befehl SelRun ()) gestartet wurde. Erfolgte der Selektionsstart über eine A- Prozedur oder dem Menüpunkt Bearbeiten / Selektieren / Selektion durchführen... , wird eine angehängte A+ Prozedur nicht ausgeführt.

Bei den A+ Prozeduren gibt es die Funktion SetResult() nicht. Wie kann die Prozedur nach Abfrage bei Selektionen realisiert werden?

Bei den A- Befehlen konnte mit der Funktion SetResult() in der "Prozedur nach Abfrage" bestimmt werden, ob ein Datensatz in die Selektionsmenge aufgenommen wird. Bei Verwendung einer A+ Prozedur als "Prozedur nach Abfrage" geschieht dies über den Rückgabewert der main-Funktion. Zu diesem Zweck wird eine main-Funktion mit einer Rückgabe vom Typ logic deklariert. Beim Beenden der Funktion mit true wird der Datensatz in die Selektionsmenge aufgenommen, bei false nicht.

Wie kann eine Selektionsmenge nach Werten sortiert werden, die sich erst zur Laufzeit der Selektion ergeben?

In der Ausgangsdatei der Selektion wird ein Hilfsfeld angelegt, welches während des Selektionslaufes die berechneten Werte aufnimmt. Die Sortierung der Selektion wird über dieses Feld vorgenommen. In der "Prozedur nach Abfrage" wird der berechnete Wert dem Hilfsfeld zugewiesen.

Da bei dem Zugriff auf eine Selektionsmenge der Datensatzinhalt als Grundlage für die Positionierung dient, muss der alternative Selektionsmodus vor dem Zugriff auf die Selektionsmenge eingeschaltet werden. Der alternative Modus wird mit der Funktion SelRead () in Verbindung mit der Option _SelKeyMode aktiviert.

Wie kann die Laufzeit von Selektionen verkürzt werden?

Siehe dazu den Abschnitt Selektionen optimieren .

Welchen Zweck erfüllt die Ausnahmebehandlung?

Bei vielen Anweisungen wird nach der Anweisung geprüft, ob ein zulässiges Resultat vorhanden ist. In Abhängigkeit davon werden dann entweder weitere Anweisungen durchgeführt oder das unzulässige Resultat wird in irgendeiner Form behandelt.

Diese Vorgehensweise kann Anweisungsfolgen stark verkomplizieren und auch unübersichtlicher machen. Durch die Verwendung der Ausnahmebehandlung in Form des sogenannten try-Blocks

kann dies vermieden werden. Dabei führen Verarbeitungsfehler zum Verlassen des

try

-Blocks. Der jeweilige Verarbeitungsfehler kann dann nach dem

try

-Block behandelt werden.>

Beispiel:

try
{
RecRead(1, 1, _RecLock); // Kunde lesen und sperren
RecLink(2, 1, 1, _RecLock); // Auftrag lesen und sperren
Inc(Ct.Sales, NetValue); // Umsatz erhöhen
Or.Ct.Posted # true; // Auftrag bei Kunden verbucht
RecReplace(2, 0); // Auftrag rückspeichern
RecReplace(1, 0); // Kunde rückspeichern
}

// Fehlerbehandlung
if (ErrGet() != _ErrOk)
{
...
}

Werden innerhalb des try-Blocks Funktionen aufgerufen, kann anstelle von try auch trysub verwendet werden.

Wie kann bei größeren try-Blöcken die Fehlerposition ermittelt werden?

Bei größeren try-Blöcken reicht der Fehlerwert vielfach zur anschließenden Behandlung nicht aus, da derselbe Fehlerwert an unterschiedlichen Stellen innerhalb des try-Blocks auftreten kann.

Zur Feststellung der Fehlerposition können innerhalb des try-Blocks entsprechende Markierungen, sogenannte LABELS verwendet werden.

Ein Label beginnt mit einem Doppelpunkt, gefolgt von einem Namen und bezeichnet einen bestimmten Abschnitt im try-Block, nämlich von der Anweisung nach dem Label bis zum nächsten Label.

In der Fehlerbehandlung kann dann mit der Funktion ErrPos () die Position des aufgetretenen Fehlers bestimmt werden.

Beispiel:

try
{
:Customer
CST.iNumber # 23;
RecRead(CST.F.Customer, 1, _RecLock);
...

:Order
ORD.iNumber # 341;
RecRead(ORD.F.Order, 1, _RecLock);
...
}

if (ErrGet() != _ErrOk)
{
switch (ErrPos())
{
case :Customer : ...
case :Order : ...
default : ...
}

ErrSet(_ErrOk);
}

ErrPos () liefert ein ganzzahliges Resultat, welches einem der definierten Labels entspricht.

Ist ErrPos () gleich 0, trat der Fehler vor dem ersten Label auf.

Wie können auftretende Laufzeitfehler abgefangen werden?

Ab der CONZEPT 16-Version 4.2 steht dem Programmierer eine Ausnahmebehandlung (try-Anweisung) zur Verfügung.

Laufzeitfehler führen normalerweise zu einem Abbruch der Prozedur. Innerhalb von try-Blöcken können Laufzeitfehler abgefangen werden und wie andere Fehlerwerte in der Fehlerbehandlung verarbeitet werden. Dazu muss allerdings für jeden einzelnen Typ von Laufzeitfehler das Abfangen in try-Blöcken explizit ein- und ausgeschaltet werden.

Dies geschieht durch den Befehl ErrTryCatch (). Soll beispielsweise eine Division durch Null abgefangen werden, lautet die Anweisung ErrTryCatch ( _ErrDivisionByZero , true ).

Die Einstellungen zum Abfangen von Laufzeitfehlern sind global. Damit sind Veränderungen für jeden try-Block relevant.

info

Laufzeitfehler, die außerhalb eines try -Blocks auftreten, führen immer zum Prozedurabbruch.

Wie kann erreicht werden, dass bei einem Fehler innerhalb eines try-Blocks dieser nicht verlassen wird?

In manchen Fällen muss ein Fehler direkt bei der Anweisung behandelt werden, da der Fehler Bestandteil der normalen Verarbeitung ist und nach der Behandlung die Verarbeitung der Anweisungsfolge fortgesetzt wird.

Zu diesem Zweck darf bei bestimmten Fehlerwerten der try-Block nicht verlassen werden (dies gilt vor allem bei Resultaten von Satzoperationen).

Mit dem Befehl ErrTryIgnore (int, int) kann veranlasst werden, dass der try-Block bestimmte Fehlerwerte einfach ignoriert.

Bei ErrTryIgnore () kann entweder ein einzelner Fehlerwert oder ein Bereich von Fehlerwerten angegeben werden.

Dabei wird bei jedem Aufruf von ErrTryIgnore () der zu ignorierende Fehlerwert neu gesetzt, d. h. bei mehreren ErrTryIgnore () summieren sich die Fehlerwerte nicht.

Beispiele:

// Resultat _rLocked wird ignoriert
ErrTryIgnore(_rLocked);

// Alle Resultate von RecRead werden von TRY ignoriert
ErrTryIgnore(_rLocked, _rNoRec);

// Von try werden jetzt keine Fehlerwerte mehr ignoriert
ErrTryIgnore(_ErrOk);

// Alle Fehlerwerte werden von try ignoriert
ErrTryIgnore(_ErrAll);

Die Anweisung ErrTryIgnore () muss immer innerhalb eines try-Blocks stehen.

Beim Laden eines Dialogs können die Feldpuffer nicht geleert werden.

In einem aufgerufenen Dialog sollen leere Eingabeobjekte dargestellt werden. Die dazugehörigen Felder werden in dem Ereignis EvtInit mit dem Befehl RecBufClear () gelöscht. Trotzdem sind die Eingabeobjekte mit Werten gefüllt.

Dieses Verhalten tritt nur auf, wenn vor dem Aufrufen des Dialoges der Eingabefokus auf einem RecList -Objekt oder einem Eingabe-Objekt steht. Der Eingabefokus kann erst dann in den neuen Dialog transferiert werden, wenn er angezeigt wird. Zu diesem Zeitpunkt ist das Ereignis EvtInit bereits beendet. Standardmäßig wird der selektierte Datensatz eines RecList -Objekts bzw. eines Eingabe-Objekts in die Feldpuffer übertragen, wenn der Fokus das Objekt verlässt.

Das Übertragen des selektierten Datensatzes in die Feldpuffer kann unterbunden werden, indem in der Eigenschaft LstFlags die Ausprägung _WinLstRecFocusTermReset entfernt wird.

Das Leeren der Feldpuffer kann ebenfalls in dem Ereignis EvtCreated erfolgen. Zu diesem Zeitpunkt ist der Fokuswechsel bereits erfolgt.

Das Verhalten tritt nicht auf, wenn der auf dem gleichen Rechner laufende externe Debugger die Verarbeitung des Ereignisses EvtInit unterbricht. In diesem Fall wechselt der Fokus von dem RecList-Objekt zum Debugger und erst dann wird das Ereignis EvtInit durchgeführt. Das Löschen des Feldinhaltes erfolgt also nach dem Fokuswechsel.

Wie können Funktionsaufrufe in der Programmierung verfolgt werden?

Der externe Debugger verfügt über einen Protokollbereich. Hier werden alle Meldungen, die mit dem Befehl DbgTrace () ausgegeben werden, dargestellt. Im Prozedurbereich wird im Falle eines Laufzeitfehlers oder dem Befehl DbgControl ( _DbgStop ) die angehaltene Prozedur angezeigt.

Im Protokollbereich kann neben den Trace-Meldungen, die Ein- und Aussprünge aus einer Funktion protokolliert werden. Das Einschalten der Protokollierung kann entweder im externen Debugger mit den Menüpunkten Protokoll / Aufrufe extern , Protokoll / Aufrufe intern bzw. Protokoll / Funktionsaustritt oder mit dem Befehl DbgControl ( _DbgEnter ) bzw. DbgControl ( _DbgLeave ) erfolgen.

Nicht alle in einer Prozedur deklarierten Variablen werden in der Variablenliste des externen Debuggers angezeigt.

Um eine Übersichtlichkeit zu gewährleisten, werden nur Variablen in die Liste aufgenommen, die in der Prozedur angesprochen werden.

Wie können aus CONZEPT 16 heraus registrierte Dateien gestartet werden?

Über den Befehl SysExecute () lassen sich Dokumente mit registrierten Dateierweiterungen aufrufen. In diesem Fall wird in (alpha1) ein Stern ('*') gefolgt vom Namen des Dokumentes angegeben.

SysExecute('*D:\Doc\Readme.doc', '', 0); // Starten eines Word-Dokumentes
Wie können aus CONZEPT 16 heraus Befehle der Betriebssystemshell aufgerufen werden?

Über den Befehl SysExecute () lassen sich Kommandos der Shell ausführen. Dazu muss in (alpha1) von SysExecute () der Windows-Befehlsprozessor durch die Angabe von 'cmd' gestartet werden. In (alpha2) wird der Shell-Befehl inklusive Parameter angegeben. Die möglichen Parameter des Befehlsprozessors können mit help cmd abgerufen werden.

SysExecute('cmd', '/c copy ' + _Sys->spPathTemp + '\a.dat ' + _Sys->spPathTemp + '\b.dat', 0); // Ausführen des Shell-Befehls copy
Wie kann aus CONZEPT 16 eine DFÜ-Verbindung gestartet werden.

Unter Windows-Betriebssystemen steht dafür das Programm Rasdial zur Verfügung.

SysExecute('Rasdial', '<Connection name>', 0);
Nach dem Mailversand aus CONZEPT 16 soll die Verbindung zum Mailserver automatisch beendet werden.

Unter Windows-Betriebssystemen kann bei einem installierten DFÜ-Netzwerk das Programm Rasdial verwendet werden. Dieses Programm kann mit verschiedenen Parametern aufgerufen werden, darunter auch Parameter zum Aufbau und zum Trennen von Verbindungen.

Ein Verbindungsabbau kann über den Befehl SysExecute ( 'rasdial' , '-h <entry>' , 0 ) erfolgen. In diesem Fall erfolgt der Abbau im Hintergrund. Nähere Informationen zum Programm "Rasdial" kann der Hilfe zu Windows entnommen werden.

Wie kann auf einfache Weise ein zusammengesetzter Datentyp gelöscht werden?

Einige der zusammengesetzten Datentypen (zum Beispiel font) besitzen eine ganze Reihe von Komponenten. Um eine Variable von diesem Typ zu leeren, müssen alle Komponenten auf 0 bzw. '' gesetzt werden. Dies geschieht schneller mit der Konstanten NULL.

Beispiel:

  local
{
cMoment : caltime;
rRect : rect;
}
{
...
/*
cMoment->vpYear # 0;
cMoment->vpMonth # 0;
cMoment->vpDay # 0;
cMoment->vpHours # 0;
cMoment->vpMinutes # 0;
cMoment->vpSeconds # 0;
cMoment->vpMilliseconds # 0;
*/
// Alternativ dazu
cMoment # NULL;

// rRect # RectMake(0,0,0,0);
rRect # NULL;
...
}
Kann während der Darstellung eines Dialogs eine Funktion ausgeführt werden?

Ja. Dazu wird ein asynchroner Dialog benötigt. Wird ein Dialog mit der Anweisung WinDialog () oder WinDialogRun () aufgerufen, bleibt die Verarbeitung der Funktion an dieser Stelle stehen, bis der Dialog wieder geschlossen wurde. Alle Funktionen werden in der Folge über Ereignisse aufgerufen.

Ein Dialog kann aber auch als asynchroner Dialog aufgerufen werden. Dazu wird bei der Anweisung WinDialogRun () der Parameter _WinDialogAsync angegeben. Die Verarbeitung der Funktion wird dann sofort nach der Anzeige des Dialogs fortgesetzt. Nach dem Aufruf des Dialoges muss also die Verarbeitung erfolgen.

Während der Verarbeitung kann mit dem Befehl WinDialogResult () ermittelt werden, ob der Dialog geschlossen wurde. In der Regel folgt nach der Anzeige des Dialoges eine while-Schleife, in der die Abbruchbedingung des Algorithmus und die Bedingung (tHdlDialog-> WinDialogResult () != _WinIdCancel ) abgeprüft wird. Das Resultat wird ebenfalls gesetzt, wenn ein Button -Objekt (TypeButton = _WinBtnUserBreak) gedrückt wird. In diesem Fall kann auch eine laufende Selektion unterbrochen werden. Bei einem anschließenden WinClose () wird entweder nach Beendigung des Algorithmus oder nach dem Drücken der Abbrechen-Schaltfläche der Dialog geschlossen.

Innerhalb der Schleife wird die gesamte Rechenzeit des Prozesses aufgebraucht, sodass eine Aktualisierung des Dialoges nicht möglich ist. Innerhalb der Schleife muss daher der Befehl WinSleep () stehen, um ein Update der Oberfläche zu ermöglichen.

Ein programmiertes Beispiel befindet sich in der Beispiele-Datenbank und im Abschnitt Schreiben von externen Dateien .

Wie können Informationen an eine beim Server ausgeführte Funktion übergeben werden?

Wird eine Funktion mit der Anweisung RmtCall () aufgerufen, wird die Funktion beim CONZEPT 16-Server ausgeführt. Der Server erzeugt dafür einen neuen Benutzer in der Datenbank. Dieser Benutzer hat seine eigenen Feldpuffer. Die notwendigen Informationen zur Durchführung der Funktion müssen entweder in den Parametern der Funktion übergeben, oder über die Möglichkeiten zum Informationsaustausch zwischen zwei Benutzern (siehe unten) übertragen werden.

Da die beim Server ausgeführte Funktion parallel zur Verarbeitung auf dem Client abläuft, muss eine Möglichkeit zur Synchronisation der beiden Prozesse geschaffen werden. In der einfachsten Form kann ein Datensatz angelegt und gesperrt werden, der zum Abschluss der Funktion wieder entsperrt wird.

Wie können Informationen zwischen zwei Benutzern in der Datenbank ausgetauscht werden?

Zum Informationsautausch zwischen zwei Benutzern stehen verschiedene Möglichkeiten zur Verfügung:

  • Austausch über externe Dateien Verfügen beide Clients über ein gemeinsames externes Verzeichnis mit Schreib- und Leserechten, können die entsprechenden Informationen über externe Dateien ausgetauscht werden. Dabei können unterschiedliche Formate verwendet werden (siehe Befehle für XML-Verarbeitung oder Message-Exchange-Befehle ). Hat jeder Benutzer einen Client mit Oberfläche gestartet, kann das externe Verzeichnis mit einem EvtFsiMonitor überwacht werden. Das Abholen der Daten kann aber zu einem beliebigen Zeitpunkt erfolgen.
  • Austausch über Socket-Verbindungen In diesem Fall benötigen beide Benutzer ein Fenster, für den das Ereignis EvtSocket aufgerufen wird, wenn Daten übertragen werden sollen. Das Datenformat ist wie beim Austausch über externe Dateien frei wählbar.
  • Austausch über Datensätze Die Informationen werden in Datensätzen gespeichert. Dabei kann auch eine temporäre Datei angegeben werden, wenn die Informationen nicht über einen Neustart des Datenbankprozesses hinaus erhalten bleiben müssen. Der Empfänger der Daten muss in regelmäßigen Abständen überprüfen, ob Informationen für ihn vorliegen. Das Datenformat wird durch die Datenstruktur bestimmt.
  • Austausch über zentrale Datenobjekte Der Austausch kann ebenfalls über zentrale Datenobjekte erfolgen. Die Daten werden nicht gespeichert und spätestens beim Abmelden des Benutzers, der die Daten geschrieben hat, gelöscht. Die Datenmenge ist auf maximal 64 MB begrenzt. Es können nur Zeichenketten übertragen werden.
Wann ist es sinnvoll Informationen in dynamischen Objekten zu verarbeiten?

Die Verarbeitung in dynamischen Objekten ist immer dann sinnvoll, wenn sonst die Informationen wiederholt aus der Datenbank gelesen werden müssen und kein Zugriff von anderen Clients auf diesen Datenbestand notwendig ist.

Muss der Datenbestand in einer Baum-Struktur organisiert werden, bietet sich die Organisation mit CteNode -Objekten an.

Wie wird die grafische Erweiterung aktiviert?

Die grafische Erweiterung ermöglicht Schnittstellen die Verwendung von Befehlen der Benutzeroberfläche und Druckfunktionen .

Beim SOA-Service kann die grafische Erweiterung aktiviert werden, indem in der Konfigurationsdatei der Eintrag c16_proc_extended auf Y gesetzt wird.

Bei der DLL-Schnittstelle wird die grafische Erweiterung aktiviert, wenn die c16_pgxe.dll statt der c16_pgxw.dll geladen wird.