![]() |
Einlesen einer FORTRAN Binärdatei mit Delphi
Hallo,
für eine Studienarbeit soll ein altes Fortran77 Programm nach Pascal übersetzt werden (Entwicklungsumgebung ist RadStudio XE5). Momentan hänge ich beim Einlesen von Datensätzen aus Binärdateien. Zu Testzweck habe ich in Fortran eine Subroutine angelegt, die vier Integerwerte (NPUNKT,NPVAR,NZELLE,NSTR und KPER) in eine Binärdatei schreibt. Es handelt sich um 32-Bit Integerzahlen, welche bei Fortran als Integer*4 definiert werden. Somit sollte der Fortran Integer*4 dem "normalen" Integer aus Pascal entsprechen!?! Im nachfolgenden Code-Block ist der zugehörige Fortran-Code abgebildet. Hinweis: NNET ist das Dateihandle. Das Öffnen und Schließen der Datei ist nicht Bestandteil der Subroutine OUTNET. Es geschieht an einer anderen Stelle im Programm.
Code:
Nun habe ich mit Delphi eine Prozedure geschrieben, die die erzeugte Fortran Binärdatei einlesen soll. Dabei entsteht allerdings nur Datensalat.
SUBROUTINE OUTNET
C********************************************************************** C C C C IMPLICIT C IMPLICIT DOUBLE PRECISION (A-H,O-Z) C COMMON /STEUER/ IERR,LIN,KPER,NPRINT,MPRINT,MAXP,MAXT,IRECH,IB2, , NSPG,NROT1,NROT2,NSTROM COMMON /TEXTE/ TITEL COMMON /NETZP/ NPMAX,NPUNKT,NPVAR,NZMAX,NZELLE,NSMAX,NSTR,MMAX COMMON /NETZD/ X(10000),Y(10000),A(10000),NUE(20000),SD(20000), , GAM(20000), , D(20000),GK(3,20000),NACHBZ(60000),ENDZ(10000), , NPER(10000),MGEB(20000), , ECKP(3,20000),RANDZ(3,20000),RANDS(3,20000), , AP(30000),EP(30000),GL(30000),GR(30000), , RAND(30000),ZL(30000),ZR(30000) COMMON /KONTUR/ GECKP(3,4000),IGECKP COMMON /IODEF / VERSIO,NTEST,IKART,NLIST,ND1,INET,NNET,NPLOT, , NUEPL C DOUBLE PRECISION NUE,GAM COMPLEX*16 A,SD CHARACTER*80 TITEL REAL*8 VERSIO INTEGER*4 NACHBZ,ENDZ,NPER,MGEB,ECKP,RANDZ,RANDS INTEGER*4 AP,EP,GL,GR,RAND,ZL,ZR C WRITE (NNET) NPUNKT,NPVAR,NZELLE,NSTR,KPER C RETURN END Zuvor habe ich schon mit der Klasse TFileStream gearbeitet und konnte erfolgreich Binärdateien anlegen und einlesen.:wink: Ich vermute daher, dass Fortran die Binärdatei anders strukturiert? Im Internet konnte ich keine Informationen dazu finden. MFG Tenobaal
Delphi-Quellcode:
procedure TNetgen.ReadNet(Directory,Filename:String);
var Stream:TFileStream; Begin Stream:=TFileStream.Create(Directory+Filename+'.NET',fmOpenRead); try try //NPUNKT,NPVAR,NZELLE,NSTR,KPER sind vom Datentyp Integer Stream.Read(NPUNKT,SizeOf(NPUNKT)); Stream.Read(NPVAR,SizeOf(NPVAR)); Stream.Read(NZELLE,SizeOf(NZELLE)); Stream.Read(NSTR,SizeOf(NSTR)); Stream.Read(KPER,SizeOf(KPER)); except on E: Exception do begin ErrorMessagesINOUT.Add('Fehler beim anlegen der Netzdatei ('+Filename+'.NET)'); ErrorMessagesINOUT.Add(E.ClassName+' Fehler aufgetreten mit Nachricht : '+E.Message); end; end; finally Stream.Free; end; End; |
AW: Einlesen einer FORTRAN Binärdatei mit Delphi
Zitat:
|
AW: Einlesen einer FORTRAN Binärdatei mit Delphi
Also... Ich mache sowas immer so:
Delphi-Quellcode:
So kannst Du schön mit dem Debugger durch gehen und im ganz schnell alles ändern.
{$A1} // Wichtig
Const Recordlaenge = 2245; // Filesize / Recordlaenge passt! type TStruct = Record A : Byte; // Bin ich mir sicher B : Word; // Bin ich mir sicher C : Byte; // Bin ich mir sicher Rest : Array[1..Recordlaenge-4 {Sizeof A-C} ] of Byte; end; var F : File; Procedure Readit; var Struct : TStruct; begin Assignfile(F,'Bla.dat'); Reset(F,1); Blockread(F,Struct,sizeof(Struct)); Closefile(F); end; Mavarik |
AW: Einlesen einer FORTRAN Binärdatei mit Delphi
Ich habe mit Delphi und Fortran jweils die Integerzahl "735" (Dezimal) in eine Binärdatei geschrieben. Beide Dateien wurden dann mit nem Hex-Editor geöffnet.
Fortran:
Code:
Delphi:
04 DF 02 00 00 04
Code:
Ich habe zuvor noch nie mit einem Hex-Editor gearbeitet :oops: ,aber meine "735" Dezimal entspricht 2DF (Hex). 2DF steht allerdings in keiner der Dateien :shock:
DF 02 00 00
Abgesehen davon, ist im Fortran-Code am Anfang und am Ende jeweils zusätzlich ein '04' (Hex) vorhanden. Laut ASCII-Tabelle bedeutet dies dem Zeichen 'EOT' - End of Transmission. Ich versuche mal die Position beim lesen der Datei zu verändern, sodass meine Delphi Prozedur erst beim 2 Byte anfängt die Daten zu lesen. |
AW: Einlesen einer FORTRAN Binärdatei mit Delphi
Deine 00 00 02 DF ist doch die DF 02 00 00, nur zweimal umgedreht, oder?
(00 00) (02 DF) -> (02 DF) (00 00) -> (DF 02) (00 00) |
AW: Einlesen einer FORTRAN Binärdatei mit Delphi
Problem gelöst. Ich habe "Stream.Position:=1;" vor den ersten Read-Befehl meiner Delphi Prozedur gehangen, damit erst ab dem 2 Byte gelesen wird :lol:
|
AW: Einlesen einer FORTRAN Binärdatei mit Delphi
Zitat:
|
AW: Einlesen einer FORTRAN Binärdatei mit Delphi
War auch zu früh gefreut. Bei mehreren Datentypen in der Binärdatei funktioniert die Lösung nicht mehr. Scheinbar werden weitere EOT-Zeichen wahllos eingestreut :wink:
|
AW: Einlesen einer FORTRAN Binärdatei mit Delphi
.. kann es sein, dass jedes Integer*4 Datenfeld mit 04 beginnt oder ist es ein Feldtrenner?
Code:
Anhand der wenigen Daten ist das schwer zu erkennen.
04 DF 02 00 00 04
Grüße Klaus |
AW: Einlesen einer FORTRAN Binärdatei mit Delphi
Zitat:
|
AW: Einlesen einer FORTRAN Binärdatei mit Delphi
Zitat:
Gruß K-H |
AW: Einlesen einer FORTRAN Binärdatei mit Delphi
Hier eine Funktion die eine Double, zwei Integer Zahlen und ein Array der Länge 3 vom Datentyp Char in eine Binärdatei schreibt.
Inhalt der Binärdatei aus dem Hexeditor:
Code:
Programmcode:
13000000000000F03F020000000200000041424313
Code:
SUBROUTINE OUTNET
C********************************************************************** C C C C IMPLICIT C IMPLICIT DOUBLE PRECISION (A-H,O-Z) C COMMON /STEUER/ IERR,LIN,KPER,NPRINT,MPRINT,MAXP,MAXT,IRECH,IB2, , NSPG,NROT1,NROT2,NSTROM COMMON /TEXTE/ TITEL COMMON /NETZP/ NPMAX,NPUNKT,NPVAR,NZMAX,NZELLE,NSMAX,NSTR,MMAX COMMON /NETZD/ X(10000),Y(10000),A(10000),NUE(20000),SD(20000), , GAM(20000), , D(20000),GK(3,20000),NACHBZ(60000),ENDZ(10000), , NPER(10000),MGEB(20000), , ECKP(3,20000),RANDZ(3,20000),RANDS(3,20000), , AP(30000),EP(30000),GL(30000),GR(30000), , RAND(30000),ZL(30000),ZR(30000) COMMON /KONTUR/ GECKP(3,4000),IGECKP COMMON /IODEF / VERSIO,NTEST,IKART,NLIST,ND1,INET,NNET,NPLOT, , NUEPL DOUBLE PRECISION NUE,GAM COMPLEX*16 A,SD,AComplex CHARACTER*3 TITEL REAL*8 VERSIO INTEGER*4 NACHBZ,ENDZ,NPER,MGEB,ECKP,RANDZ,RANDS INTEGER*4 AP,EP,GL,GR,RAND,ZL,ZR ADOUBLE=1.0 !Double 64-Bit NINTEGER=2 !Integer 32-Bit TITEL='ABC' ! Array of Char WRITE (NNET) ADOUBLE,NINTEGER,NINTEGER,TITEL !Schreibe in Binärdatei RETURN END |
AW: Einlesen einer FORTRAN Binärdatei mit Delphi
Die $13 sind offensichtlich die Länge des Records (19 Zeichen). Warum die am Ende nochmal kommt, weiß ich auch nicht. Mach doch mal eine Datei mit mehreren Datensätzen. (Mit Fortran hatte ich das letzte Mal vor 40 Jahren zu tun...)
|
AW: Einlesen einer FORTRAN Binärdatei mit Delphi
Zitat:
@tenobaal: Du scheinst den implementierungsabhängigen Unformatted-Mode zu benutzen, insofern wirst du keine Lösung finden, die mit allen Fortran-Compilern kompatibel ist. |
AW: Einlesen einer FORTRAN Binärdatei mit Delphi
Zitat:
|
AW: Einlesen einer FORTRAN Binärdatei mit Delphi
Also für mich sieht das ganz und gar nicht nach eine Binärdatei aus...
![]() Schau Dir mal das hier an... Mavarik |
AW: Einlesen einer FORTRAN Binärdatei mit Delphi
ich würde das im Prinzip so aufbauen:
Delphi-Quellcode:
unit FortranWriter;
interface type TFortranObjectWriter = abstract class(TObject) protected class procedure GetDataObjectClass: TClass; virtual; abstract; class procedure WriteToBuffer(ABuffer: TCustomStream; AValue: Double); overload: class procedure WriteToBuffer(ABuffer: TCustomStream; AValue: Integer); overload: class procedure Register; class procedure UnRegister; public class property DataObjectClass: TClass read GetDataObjectClass; class procedure WriteObjectToBuffer(ABuffer: TCustomStream; AObject: TObject); virtual; abstract; end; TFortranObjectWriterClass = class of TFortranObjectWriter; TFortranWriter = class(TObject) constructor Create(AStream: TCustomStream); destructor Destroy; private FBuffer: TMemmoryStream; FStream: TCustomStream; class var FObjectWriter: TClassList; protected class procedure RegisterObjectWriter(AClass: TFortranObjectWriterClass); class procedure UnRegisterObjectWriter(AClass: TFortranObjectWriterClass); class procedure GetObjectWriter(AClass: TClass): TFortranObjectWriterClass; public procedure Write(AObject: TObject); virtual; end; implementation constructor TFortranWriter.Create(AStream: TCustomStream); begin inherited Create; FStream := AStream; FBuffer := TMemmoryStream.Create; end; destructor TFortranWriter.Destroy; begin FBuffer.Free; inherited; end; procedure TFortranWriter.Write(AObject: TObject); var ObjectWriter: TFortranObjectWriterClass; ByteSize: Byte; begin FBuffer.Clear; ObjectWriter := GetObjectWriter(AObject.Class); if not Assigned(ObjectWriter) then raise Exception.Create('...'); ObjectWriter.WriteObjectToBuffer(FBuffer, AObject); if FBuffer.Size = 0 then raise Exception.Create('...'); if FBuffer.Size > 255 then raise Exception.Create('...'); ByteSize := FBuffer.Size; FStream.Write(ByteSize, SizeOf(ByteSize)); FBuffer.WriteToStream(FStream); FStream.Write(ByteSize, SizeOf(ByteSize)); end; class procedure TFortranWriter.RegisterObjectWriter(AClass: TFortranObjectWriterClass); begin if not Assigned(FObjectWriter) then FObjectWriter := TClassList.Create; FObjectWriter.Add(AClass); end; class procedure TFortranWriter.UnregisterObjectWriter(AClass: TFortranObjectWriterClass); begin if not Assigned(FObjectWriter) then Exit; FObjectWriter.Remove(AClass); if FObjectWriter.Count = 0 then FreeAndNil(FObjectWriter); end; class procedure TFortranWriter.GetObjectWriter(AClass: TClass): TFortranObjectWriterClass; begin if Assigned(FObjectWriter) then begin for i := FObjectWriter.Count - 1 downto 0 do begin Result := TFortranObjectWriter(FObjectWriter[i]); if AClass is Result.DataObjectClass then Exit; end; end; Result := nil; end; class procedure TFortranObjectWriter.WriteToBuffer(ABuffer: TCustomStream; AValue: Double); begin ABuffer.Write(AValue, SizeOf(AValue)); end; class procedure TFortranObjectWriter.WriteToBuffer(ABuffer: TCustomStream; AValue: Integer); begin ABuffer.Write(AValue, SizeOf(AValue)); end; class procedure TFortranObjectWriter.Register; begin TFortranWriter.Register(Self); end; class procedure TFortranObjectWriter.UnRegister; begin TFortranWriter.UnRegister(Self); end; end.
Delphi-Quellcode:
unit MyDataObject;
interface type TMyDataObject = class(TObject) private FA: Double; FB: Integer; FC: Integer; published property A: Double read FA write FA; property B: Integer read FB wirte FB; property C: Integer read FC write FC; end;
Delphi-Quellcode:
unit FortranMyDataObject;
interface implementation type TMyDataObjectFortranWriter = class(TFortranObjectWriter) protected class procedure GetDataObjectClass: TClass; override; public class procedure WriteObjectToBuffer(ABuffer: TCustomStream; AObject: TObject); override; end; class procedure TMyDataObjectFortranWriter.GetDataObjectClass: TClass; begin Result := TMyDataObject; end; class procedure TMyDataObjectFortranWriter.WriteObjectToBuffer(ABuffer: TCustomStream; AObject: TObject); begin with TMyDataObject(AObject) do begin WriteToBuffer(ABuffer, A); WriteToBuffer(ABuffer, B); WriteToBuffer(ABuffer, C); end; end; initialization TMyDataObjectFortranWriter.Register; finalization TMyDataObjectFortranWriter.UnRegister; end.
Delphi-Quellcode:
MyObject := nil;
Stream := nil; Writer := nil; try Stream := TFileStream.Create('MyFortranFile.bin'); Writer := TFortranWriter.Create(Stream); MyObject := TMyDataObject.Create; MyObject.A := 1; MyObject.B := 2; MyObject.C := 3; Writer.Write(MyObject); MyObject.A := 4; MyObject.B := 5; MyObject.C := 6; Writer.Write(MyObject); finally Writer.Free; Stream.Free; MyObject.Free; end; |
AW: Einlesen einer FORTRAN Binärdatei mit Delphi
Zitat:
Code:
Lassen wir mal die $13 vor und nach dem Datensatz außer acht...
13 00 00 00 00 00 00 F0 3F 02 00 00 00 02 00 00 00 41 42 43 13
Dann schreibt diese kleine Delphi Programm das gleiche weg... (Oder liest es ein). Auch ohne eine Monster-Class dafür zu Programmieren. :stupid:
Delphi-Quellcode:
Aber das wird trotzdem nicht zielführend sein, da der Text ABC keine längen Informationen hat...
unit Unit1;
interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls; {$A1} type TB = record Len1 : byte; A : Double; C, D : Integer; S : array[1..3] of AnsiChar; Len2 : byte; end; // 13 00 00 00 00 00 00 F0 3F 02 00 00 00 02 00 00 00 41 42 43 13 TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private-Deklarationen } B : TB; public { Public-Deklarationen } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var F : File; begin Assignfile(F,'Test.net'); rewrite(F,1); B.Len1 := $13; B.Len2 := $13; B.A := 1.0; B.C := 2; B.D := 2; B.S[1] := 'A'; B.S[2] := 'B'; B.S[3] := 'C'; Blockwrite(F,B,Sizeof(B)); Closefile(F); end; end. Es sei den, Texte werden mit einem $13 beendet.. Also kein längenbyte und nicht 0 terminiert. Oder das letzte $13 ist einfach ein CR weil Datei nicht als Bin Datei geöffnet wurde. Boh... Mach doch mal ein richtiges Beispiel und fülle die Daten mit einem Pattern.
Delphi-Quellcode:
In Fortran natürlich und bitte 3x hinter einander weg schreiben... und bitte erst dem Link aus meinen mein
var
B : Byte; W : Word; L : Integer; S1 : String S2 : String; N : Byte; begin B := $55; W := $3388; L := $11223344; S1 := 'Das ist ein cooler Text'; S2 := 'Noch einer'; N := $99; ... end; ![]() Mavarik |
AW: Einlesen einer FORTRAN Binärdatei mit Delphi
Zitat:
|
AW: Einlesen einer FORTRAN Binärdatei mit Delphi
Zitat:
![]() ![]() Mavarik :coder: |
AW: Einlesen einer FORTRAN Binärdatei mit Delphi
Der Wiki-Eintrag ist an dieser Stelle irreführend. Es ist wirklich jede Datei eine Binärdatei. Textdateien sind eine Untermenge davon und auch davon gibt es wieder verschiedene Untermengen mit diversen Encodings und Line-Endings.
Fragte doch neulich jemand, warum denn beim Einlesen mit TEncoding.ANSI keine Fehlermeldung käme, wenn man dem Programm eine Unicode-Datei unterjubelt. Dein BlockRead-Record-Ansatz ist zwar ziemlich Old-School, aber würde hier durchaus funktionieren - wenn die Datensätze wirklich alle gleich lang sind. Das ist aber nicht zwingend der Fall. Unter der Annahme, daß das eine Byte vor und nach den Nutzdaten wirklich die Recordlänge angibt (255 Byte Maximum - unglaublich!), dann würde sich eigentlich eher ein Stream-Wrapper oder ein
Delphi-Quellcode:
anbieten.
class helper for TStream
|
AW: Einlesen einer FORTRAN Binärdatei mit Delphi
Zitat:
Eine Binärdatei ist für mich eine 1:1 Entsprechungen eine Speicher-Datenstruktur ohne ausgerichtete Felder -{$A1}. Keine längen Bytes, Trennzeichen, kein ^Z am Ende usw. Wenn ich ein Byte in eine Datei schreibe, muss die Datei auch nur ein Byte lang sein. Wenn ich 100 Datensätze mit je 15 Byte weg schreibe muss die Datei auch 1500 Bytes enthalten. Nicht mehr und nicht weniger. Dann ist es in meinem Sprachgebrauch eine Binärdatei... Mavarik |
AW: Einlesen einer FORTRAN Binärdatei mit Delphi
Danke euch, ihr habt mir schon sehr weitergeholfen :thumb:
Ich bin heute auf den Beitrag gestoßen ![]() Zumindest verstehe ich nun, wie die Struktur der Dateien aufgebaut ist. |
AW: Einlesen einer FORTRAN Binärdatei mit Delphi
Zitat:
Man muss doch mit Streams auch nicht gleich eine riesige, overengineerte Monster-Class programmieren. (Generics sind allerdings ganz nett in dem Fall, meine ich. Kann sein, dass die Syntax falsch ist, ich bin dank der Preise der neuen Delphi-Versionen leider nie in den Genuss gekommen.)
Delphi-Quellcode:
(Fehlerbehandlung sollte da natürlich noch rein, hat dein Beispiel aber auch nicht).
type
TFortranStream = class protected FStream: TStream; public constructor Create(Stream: TStream); procedure Read<T>(var Rec: T); procedure Write<T>(const Rec: T); end; implementation {$R *.dfm} constructor TFortranStream.Create(Stream: TStream); begin FStream := Stream; end; procedure TFortranStream.Read<T>(var Rec: T); var Len: Byte; begin FStream.Read(@Len, sizeof(Byte)); FStream.Read(@Rec, sizeof(T)); end; procedure TFortranStream.Write<T>(const Rec: T); var Len: Byte; begin Len := sizeof(T); FStream.Write(@Len, sizeof(Len)); FStream.Write(@Rec, sizeof(Rec)); FStream.Write(@Len, sizeof(Len)); end; type TFortranRecord = packed record A : Double; C, D : Integer; S : array[1..3] of AnsiChar; end; procedure TForm1.Button1Click(Sender: TObject); var F : File; FileStream: TFileStream; Fortran: TFortranStream; Rec: TFortranRecord; begin FileStream := TFileStream.Create('Test.net'); Fortran := TFortranStream.Create(FileStream); Rec.A := 1.0; Rec.C := 2; Rec.D := 2; Rec.S[1] := 'A'; Rec.S[2] := 'B'; Rec.S[3] := 'C'; Fortran.Write<TFortranRecord>(Rec); Fortran.Free; FileStream.Free; end; |
AW: Einlesen einer FORTRAN Binärdatei mit Delphi
Ich persönlich würde das über einen Stream lösen und jedes Feld einzeln einlesen. Das ist nur unwesentlich komplexer als ein Blockread mit Record, aber man sieht genau, was Sache ist. Letztendlich ist es Geschmackssache, denn wenn man den Record entsprechend deklariert, sieht man genauso, was Sache ist.
|
AW: Einlesen einer FORTRAN Binärdatei mit Delphi
Zitat:
Ja natürlich Blockwrite... Wieso auch nicht.. Abgesehen davon das es Blockbezogen fast 100%ig das gleiche ist (vielleicht ein klein wenig weniger overhead). Ein Blockwrite ruft folgendes auf: 1. Function _blockwrite 2. BlockIO 3. IOProc (Kernel32 "Writefile"); "Dein Stream" ruft folgendes auf: 1. TStream.Write 2. Filewrite 3. WriteFile (Kernal32 "Writefile"); Lassen wir mal außer acht, dass der Blockwrite wenige Taktcycle weniger braucht. Also macht "mein" alten Pascal-Files genau das gleiche... Noch Fragen? |
AW: Einlesen einer FORTRAN Binärdatei mit Delphi
Kannst du mit deinem Blockwrite/Blockread mal eben nachträglich auf eine andere Datenquelle umsteigen, z.B. einen MemoryStream?
Ich hab nicht grundsätzlich etwas gegen althergebrachte Methoden, aber Streams sind hier meiner Meinung nach in jeder Hinsicht überlegen. Man braucht ja nicht mal mehr Code – man kann mit Streams – wenn man will – genau so programmieren wie mit Pascal-Files. Aber man hat halt zusätzlich noch mehr Optionen. Zitat:
Viel relevanter für die Performance ist, wie groß die Blöcke sind, die man einliest oder schreibt, und da würde ich wenn dann vermuten, dass das modernere API moderne Hardware besser auslastet. |
AW: Einlesen einer FORTRAN Binärdatei mit Delphi
Zitat:
|
AW: Einlesen einer FORTRAN Binärdatei mit Delphi
Zitat:
Zitat:
Aber das hat jetzt echt nix mehr mit diesem Thread zu tun... Also würde sagen, wir beenden das hier... |
AW: Einlesen einer FORTRAN Binärdatei mit Delphi
Ein kleiner Nachsatz sei mir gestattet. Bis XP SP2..... (gefühlt) war das Block.. schneller als der Filestream (mit entsprechender Buffersize!) danach war es umgekehrt, darum hab ich längst alle Block.. durch TFilestream ersetzt.
Und "Monsterklassen" sind dafür auch nicht nötig.
Delphi-Quellcode:
Gruß
fs.create(....,...);
repeat gelesen:=fs.read(buffer,zulesen); until gelesen<zulesen; fs.free; K-H |
Alle Zeitangaben in WEZ +1. Es ist jetzt 00:05 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz