Delphi-PRAXiS
Seite 1 von 3  1 23      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Applikation initialisert "falsche" Unit (https://www.delphipraxis.net/187844-applikation-initialisert-falsche-unit.html)

ZOD 8. Jan 2016 10:03

Applikation initialisert "falsche" Unit
 
Hintergrund:
D7 und Jedi Debug (aus "JVCL345CompleteJCL231-Build4197") - Windows als Ziel OS
Datenbankanbindung (via SqlExpr an Firebird DB)

Ich suche nun schon geraume Zeit nach einem Problem - folgender Rahmen:

wir haben hier eine Anwendung im Bereich Fertigungssteuerung/Planung. Sie ist in mehrere Applikationen aufteilt. Es gibt eine Exe als GUI für den Werkstattbereich und eine zweite Exe für die Arbeitsvorbereitung und Stammdatenpflege (AV). Naturgemäß sind viel Units in beiden Anwendungen eingebunden. Das war bisher kein Problem.

Seit einer Woche (nach irgendeiner Änderung von mir, die ich nicht erkenne/finde) habe ich nun das Problem, das beim Start des Werkstattmoduls zuallererst das Hauptformular des AV-Modul initialisiert wird.

Die DPR des Werkstattmodul sieht so aus:

Delphi-Quellcode:
program RMWerk;

{%File '..\Tools\FastMM\FastMM4Options.inc'}

uses
  FastMM4 in '..\Tools\FastMM\FastMM4.pas',
  FastMM4Messages in '..\Tools\FastMM\FastMM4Messages.pas',
  RtlVclOptimize in '..\Tools\RtlVclOptimize\RtlVclOptimize.pas',
  MidasSpeedFix in '..\Tools\MidasSpeedFix\MidasSpeedFix.pas',
  Forms,
  uMainWerk in 'uMainWerk.pas' {frmMainWerk},
  dlg_LAGAusw in 'dlg_LAGAusw.pas' {frmWahlLAG},
  Dlg_LogFile in 'Dlg_LogFile.pas' {frmDlgLogFile},
  Dlg_StartBuchung in 'Dlg_StartBuchung.pas' {frmDlgStartBuchung},
  Dlg_StopBuchung in 'Dlg_StopBuchung.pas' {frmDlgStopBuchung},
  dm_Buchung in 'dm_Buchung.pas' {dmBuchung: TDataModule},
  dm_Werk in 'dm_Werk.pas' {dmWerk: TDataModule},
  uWerkGlob in 'uWerkGlob.pas',
  Dm_Connection in '..\Prog\Dm_Connection.pas' {DmConnection: TDataModule},
  RMGlobFunc in '..\Tools\RMGlobFunc.pas',
  uRMIni in '..\Prog\uRMIni.pas',
  RMGlob in '..\Tools\RMGlob.pas',
  Dm_GlobData in '..\Prog\Dm_GlobData.pas' {DmGlobData: TDataModule},
  ufrmUrRM in '..\Prog\ufrmUrRM.pas' {frmUrRM},
  RMErrorTool in '..\Tools\RMErrorTool.pas',
  uRM_JEDI_ExceptionDialog in '..\Prog\uRM_JEDI_ExceptionDialog.pas' {ExceptionDialog},
  ufrmRMPasBugReport in '..\Prog\ufrmRMPasBugReport.pas' {frmRMPasBugReport},
  uRMMeldungsDialog in '..\Tools\uRMMeldungsDialog.pas' {RMMeldungsDialog},
  ufrmRMPKzuFAPAGZuordnung in '..\Prog\ufrmRMPKzuFAPAGZuordnung.pas' {frmRMPKzuFAPAGZuordnung},
  RMFormTool in '..\Tools\RMFormTool.pas' {dmRMFormTool: TDataModule},
  RMContainer in '..\Prog\RMContainer.pas',
  uallProcess in '..\Tools\uallTools\uallProcess.pas',
  uallDisasm in '..\Tools\uallTools\uallDisasm.pas',
  uallDisasmEx in '..\Tools\uallTools\uallDisasmEx.pas',
  uallHook in '..\Tools\uallTools\uallHook.pas',
  uallKernel in '..\Tools\uallTools\uallKernel.pas',
  uallProtect in '..\Tools\uallTools\uallProtect.pas',
  uallTableHook in '..\Tools\uallTools\uallTableHook.pas',
  uallUtil in '..\Tools\uallTools\uallUtil.pas',
  Dm_dokumente in '..\Tools\Dm_dokumente.pas' {dmDokumente: TDataModule},
  RMPAS_FastMMUsageTracker in '..\Tools\FastMM\RMPAS_FastMMUsageTracker.pas' {fFastMMUsageTracker},
  ufrmRMInfoPPBaumVergleich in '..\Prog\ufrmRMInfoPPBaumVergleich.pas' {frmRMInfoPPBaumVergleich},
  RTTIUnit in '..\Tools\RTTI\RTTIUnit.pas',
  Dm_RMTV_Menu in '..\Prog\Dm_RMTV_Menu.pas' {DMTV_Menu: TDataModule},
  dm_AutoLogin in '..\Tools\dm_AutoLogin.pas' {dmAutoLogin: TDataModule},
  DM_GefaehrdungsVerwaltung in '..\Prog\DM_GefaehrdungsVerwaltung.pas' {DMGefaehrdungsVerwaltung: TDataModule},
  Dm_RMSB_Menu in '..\Prog\Dm_RMSB_Menu.pas' {DMRMSB_Menu: TDataModule},
  ufrmRMStammGefAVPlazet in '..\Prog\ufrmRMStammGefAVPlazet.pas' {frmRMStammGefAVPlazet},
  VCLFixes in '..\Tools\VCLFixes.pas';

{$R *.res}
{$R ..\Tools\WindowsXP.RES}

begin
  Application.Initialize;
  Application.Title := 'PASwerk';
  Application.CreateForm(TfrmMainWerk, frmMainWerk);
  Application.Run;
end.
Nun wird jedoch vor bzw. bei Application.Initialize das initialization der Unit uMain (das ist das Hauptformular des AV-Moduls) aufgerufen. Das sieht so aus:

Delphi-Quellcode:
initialization

  if not JclHookExceptions then
    MessageDlg('Fehler beim Initialisieren der Exception-Behandlung', mtError, [mbOK], 0);
  // Enable raw mode (default mode uses stack frames which aren't always generated by the compiler)
  Include(JclStackTrackingOptions, stRawMode);
  // Disable stack tracking in dynamically loaded modules (it makes stack tracking code a bit faster)
  Include(JclStackTrackingOptions, stStaticModuleList);
  Include(JclStackTrackingOptions, stTraceAllExceptions);
  // Initialize Exception tracking
  if (not JclStartExceptionTracking) then
    MessageDlg('Fehler beim Instanzieren des Exception-Tracking (Fehlerverfolgung)!', mtWarning, [mbOK], 0);;

  OleInitialize(nil);
Der Aufruf von
Delphi-Quellcode:
JclStartExceptionTracking
liefert ein "False" - klar, es ist ja auch das Werkstattmodul gestartet.

Meine Frage nun:
kann mir jemand einen Tipp geben, WARUM das inizialization der Unit uMain (AV-Modul) überhaupt bei Application.Initialize im Werkstatt-Modul aufgerufen wird? Nach meinem Verständnis dürfte dies gar nicht aufgerufen werden.

Die Unit uMain ist NICHT im uses - es existiert lediglich eine indirekte Abhängigkeit, die bisher nie zum o.g. Problem führt.

Ich hoffe, meine verwirrten Gedanken haben eine einirgmaßen verständliche Situationsbeschreibung erlaubt ..

mkinzler 8. Jan 2016 10:18

AW: Applikation initialisert "falsche" Unit
 
Vielleicht wäre noch uMainWerk.pas interessant. Bzw. die Unit in welcher im Initialsierungteil ggf. auf die andere Unit zugegriffen wird.

ZOD 8. Jan 2016 10:34

AW: Applikation initialisert "falsche" Unit
 
das sind recht große Units und viel Code drumrum - ich habe mal die uses rausgezogen, vielleicht hilft das schon.

Also die uses von uMainWerk bis zum type:

Delphi-Quellcode:
unit uMainWerk;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Buttons, ComCtrls, ExtCtrls, ActnList, StdCtrls, Menus,
  Provider, DB, DBClient, Grids, DBGrids, Mask, DBCtrls, ToolWin,
  ImgList, SQLTimSt, DateUtils, dm_AutoLogin, uWerkGlob, RMSQLDataSet,
  ufrmUrRM,
  //FormRoller,
  DM_GlobData,
  //JvComponentBase, JvExControls, JvXPCore, JvXPButtons, JvSpeedButton
  JvComponentBase, JvCaptionButton, JvExControls, JvXPCore, JvXPButtons,
  JvExExtCtrls, JvExtComponent, JvXPCheckCtrls, JvSpeedButton,
  RMContainer,
  uIntList,
  l14, cmbtll14, cmbtls14,
  ZodPageControl,
  ZodDBGrid,
  RMGlob,
  uRMObjects,
  Dm_RMTV_Menu,
  Dm_RMSB_Menu,
  JvExDBGrids, JvDBGrid, JvDBUltimGrid, JvLabel, FMTBcd, SqlExpr, TB2Item,
  TB2Dock, TB2Toolbar, JvImage, JvExStdCtrls, JvMemo, TB2ToolWindow,
  AppEvnts
  ;
const
  CON_MENGENTYP_PP = 0;
  CON_MENGENTYP_QC = 1;
  CON_MENGENTYP_KEINE = 2;
  CON_NAMEPREFIX_PPINFOTV_TOOLBAR = 'TBToolbarPPInfoLAG';
  CON_NAMEPREFIX_PPINFOTV_TOOLWINDOW = 'TBToolWindowPPInfoLAG';
  CON_NAMEPREFIX_PPINFOTV_MEMZEITSTEMPEL = 'MemZeitStempelPPInfoLAG';
  CON_MITPPINFOANZEIGE = FALSE;
  CON_MITPPVERGLEICH = FALSE;

  CON_UMAINWERK_STATUSBAR_PANELNO_PROGVERSION = 0;
  CON_UMAINWERK_STATUSBAR_PANELNO_DATUM = 1;
  CON_UMAINWERK_STATUSBAR_PANELNO_GEF = 2;
  CON_UMAINWERK_STATUSBAR_PANELNO_QM_STATISTIK = 3;
  CON_UMAINWERK_STATUSBAR_PANELNO_BENUTZER = 4;

type
  TfrmMainWerk = class(TfrmUrRM)
und hier das gleiche von uMain:

Delphi-Quellcode:
unit uMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, ComCtrls, ActnList, ImgList, ToolWin, Menus,
  FMTBcd, DB, SqlExpr, RMGlob, dm_MakeTree, uRMObjects, ufrmUrRM, Fortschr,
  RMSQLDataSet, RMBasisObject, sdl_SDLColors, ShellApi, CorEdit, mylabel,
  ActiveX, ShlObj, ComObj, Provider, DBClient, Buttons,
  Dm_GlobData, Dm_RMTV_Menu, AppEvnts,
  JvComponentBase, JvCaptionButton, JvExControls, JvXPCore, JvXPButtons,
  JvExExtCtrls, JvExtComponent, JvClock, JvXPCheckCtrls,
  DragDrop, DropSource, DragDropFile, OleCtrls,
  PdfLib_TLB, DM_Connection_MLaunch, JvExComCtrls, JvCoolBar, TB2Item,
  TB2Dock, TB2Toolbar, TB2ExtItems, TB2ToolWindow, JvComCtrls,
  JvCheckTreeView, Grids, DBGrids, JvExDBGrids, JvDBGrid, JvDBUltimGrid,
  ZodPageControl, ZodDBGrid,
  uRMPopupListEx, VirtualTrees, JvLabel
  ;

const
  CON_UMAIN_STATUSBAR_PANELNO_BENUTZER = 0;
  CON_UMAIN_STATUSBAR_PANELNO_LOGIN = 1;
  CON_UMAIN_STATUSBAR_PANELNO_SUPERVISOR = 2;
  CON_UMAIN_STATUSBAR_PANELNO_GEF = 3;
  CON_UMAIN_STATUSBAR_PANELNO_QM_STATISTIK = 4;
  CON_UMAIN_STATUSBAR_PANELNO_NODE_STATUS = 5;
  CON_UMAIN_STATUSBAR_PANELNO_NODE_ICON = 6;
  CON_UMAIN_STATUSBAR_PANELNO_NODE_TEXT = 7;
  CON_UMAIN_STATUSBAR_PANELNO_STATUSINFO = 8;

type
  TfrmMain = class(TfrmUrRM, IDropSource)
Die indirekte Abhängigkeit zu uMain ist in zwei Units gegeben: RMGlobFunc und uRMIni
Hier die uses von RMGolbFunc:
Delphi-Quellcode:
unit RMGlobFunc;

interface

uses
  classes, SysUtils, Forms, Controls, ShellAPI, StrUtils,
  Graphics, sdl_SDLColors, uIntList, CheckLst, MAPI,
  StdCtrls, ActiveX, ShlObj, ComObj, ComCtrls,
  JvComponentBase, JvCaptionButton, JvExControls, JvXPCore, JvXPButtons,
  JvExExtCtrls, JvExtComponent, JvXPCheckCtrls, JvSpeedButton,
  RMSQLDataSet,Types, Menus, ActnList, Printers, DBClient, DB, DBGrids, JPEG,
  WinSock, Grids, ExtCtrls, CorEdit,
  TB2Item, TB2Toolbar,
  RMBasisObject,
  uRMObjects,
  uRMPing,
  uRMPingDialog,
  RMGlob,
  RMGlobFuncUniversal,
  l14
  ;

type
  TRMGlobFuncException = class(TRMGlobFuncUniversalException);
type
  TRMGlobFunc = class(TRMGlobFuncUniversal)
und hier die uses aus dem Vorgänger RMGlobFuncUniversal:
Delphi-Quellcode:
unit RMGlobFuncUniversal;

interface

uses
  RMGlob,
  classes, SysUtils, Forms, Controls, ShellAPI, StrUtils,
  Graphics, sdl_SDLColors, uIntList, CheckLst, MAPI,
  StdCtrls, ActiveX, ShlObj, ComObj, ComCtrls,
  Types, Menus, ActnList, Printers, DBClient, DB, DBGrids, JPEG,
  WinSock, Grids, ExtCtrls, CorEdit,
  RMGlobFuncBasis, Windows, Contnrs
  //RMSQLDataSet,
  //JvComponentBase,
  //JvCaptionButton, JvExControls, JvXPCore, JvXPButtons,
  //JvExExtCtrls, JvExtComponent, JvXPCheckCtrls, JvSpeedButton,
  //TB2Item, TB2Toolbar
  ;

type
  TRMGlobFuncUniversalException = class(TRMGlobFuncBasisException);

  // Für Registrierung
  TDllRegisterServer = function: HResult; stdcall;

  // für die Dateiausführung
  TExecuteWaitEvent = procedure(const ProcessInfo: TProcessInformation;
                                    var ATerminate: Boolean) of object;



type
  TRMGlobFuncUniversal = class(TRMGlobFuncBasis)
und last but not least die uses von RMGlobFUncBasis

Delphi-Quellcode:
uses
  classes, SysUtils, Forms
  ;

type
  TRMGlobFuncBasisException = class(Exception);
  // Für Registrierung
  TRMGlobFuncBasis = class(TComponent)
Die uses der Unit uRMIni sieht so aus (Konstantendefinitionen gekürzt):

Delphi-Quellcode:
unit uRMIni;

interface

uses
  SysUtils, Classes, FMTBcd, DBClient, Provider, DB, SqlExpr, DBXpress, Controls,
  IniFiles, StrUtils, Dialogs, Forms,
  uIntList, RMGlob
  ;
const
.
.
.

type
  //----------------------------------------------------------------------------
  // Fehlerklasse für Fehler innerhalb des Ini Tools
  //----------------------------------------------------------------------------
  TRMIniException = class(Exception);

  //----------------------------------------------------------------------------
  // INI-Tool zum Ermitteln von Informationen aus der globalen INI-Datei
  //----------------------------------------------------------------------------
  TRMIni = class(TIniFile)

Christian Seehase 8. Jan 2016 10:36

AW: Applikation initialisert "falsche" Unit
 
Moin ZOD,

gerade bei einem komplexen Projekt durfte es ziemlich aussichtslos sein die Reihenfolge in der die Initialization-Abschnitte ausgeführt werden von Hand zu ermitteln.
Das entnehme ich zumindest den Ausführungen, die Hagen hier gemacht hat.
Vielleicht hilft es Dir weiter.

ZOD 8. Jan 2016 10:38

AW: Applikation initialisert "falsche" Unit
 
zur Vollständigkeit noch die uses von DmGlobData:

Delphi-Quellcode:
unit
  Dm_GlobData;

interface

uses
  SysUtils, Classes, Dialogs, ImgList, Controls, JvComponentBase, JvXPCore,
  ExtCtrls, RMGlob, Graphics, StrTools, JvInterpreter, JvInterpreterFm,
  FMTBcd, DB, DBClient, Provider, SqlExpr, RMSQLDataSet;

type
  //----------------------------------------------------------------------------
  // Fehlerklasse für Fehler innerhalb der Unit
  //----------------------------------------------------------------------------
  TDMGlobDataException = class(Exception);

  TDmGlobData = class(TDataModule)
und RMGlob:

Delphi-Quellcode:
unit RMGlob;

interface

uses
  SysUtils, Messages, types, classes, Forms, controls, Dialogs, ComCtrls,
  Graphics, sdl_SDLColors, StdCtrls, DateUtils, Contnrs,
  VirtualTrees
  ;


type
und ufrmUrRM:

Delphi-Quellcode:
unit ufrmUrRM;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, RMGlob, Printers, Math, ExtCtrls,
  StdCtrls, DBXpress, JvComponentBase, JvCaptionButton, ActnList,
  JvExExtCtrls, JvExtComponent,
  JvExControls, JvXPCore, JvXPButtons, DM_GlobData,
  DBGrids, ZodDBGrid
  ;

const
  CON_UrRM_BLANKS_FUER_CAPTION_CAPTIONBUTTON = '         ';

type
  TfrmUrRM = class(TForm)

Lemmy 8. Jan 2016 11:00

AW: Applikation initialisert "falsche" Unit
 
Zitat:

Zitat von ZOD (Beitrag 1326379)

Seit einer Woche (nach irgendeiner Änderung von mir, die ich nicht erkenne/finde) habe ich nun das Problem, das beim Start des Werkstattmoduls zuallererst das Hauptformular des AV-Modul initialisiert wird.

....

Meine Frage nun:
kann mir jemand einen Tipp geben, WARUM das inizialization der Unit uMain (AV-Modul) überhaupt bei Application.Initialize im Werkstatt-Modul aufgerufen wird? Nach meinem Verständnis dürfte dies gar nicht aufgerufen werden.

Die Unit uMain ist NICHT im uses - es existiert lediglich eine indirekte Abhängigkeit, die bisher nie zum o.g. Problem führt.

weil du du uMain in irgend einer uses stehen hast, die im Werkstattmodul Verwendung findet, ggf. wieder über div. Ecken. Es gibt in TScreen, d.h. in Forms.Screen eine Liste mit den Formularen, die wird per TScreen.AddForm gefüllt. Nimm also dein Werkstattprogramm her, compilier mit System-dcus und mach nen Haltpunkt in die Methode (AddForm), in der Du dann prüfst, wann hier die Mainform vom AV hinzugefügt wird. Und dann wird es unschön - du musst jetzt die Forms die zuvor hinzugefügt wurden überprüfen bzw. deren Uses ob hier ein Verweis auf die Hauptunit des Projektes gemacht wird, d.h. lass nen Zähler mitlaufen, dass Du die Namen der 10 FOrms vorher beim erneuten Debuggen raus finden kannst.
Ah und in System.InitUnits werden die Units durchgegangen und ein Initialization aufgerufen - das wäre sogar der bessere Ort für die Anlayse, dann bekommst Du auch die units die kein FOrmular haben.... sollte dort ein Verweis auf das Hauptformular stehen....

nahpets 8. Jan 2016 11:06

AW: Applikation initialisert "falsche" Unit
 
Sagen wir mal so:

Durch die Uses-Listen wissen wir jetzt, wo was gebraucht werden könnte.

Prinzipiell kann man in die Uses-Liste alle Units einfügen, die man so kennt oder auf der Platte hat.
Dies bedeutet nicht, dass aus diesen Units irgendwo, irgendwas "gebraucht" wird.
Soweit ich das mitbekommen habe, baut Delphi beim Kompilieren letztlich aber nur das zusammen, was auch gebraucht wird. Der Initialisierungsteil einer nicht benötigten Unit wird daher nicht in die Exe übernommen.
(Falls diese Aussage falsch ist, bitte hauen, damit ich mein Halbwissen aufbessern kann.)

Momentan gehe ich davon aus, dass aus der Unit, deren Initialisierungsteil "unerwartet früh" aufgerufen wird, schon irgendwo etwas benötigt wird, bevor die Unit uMainWerk.pas "irgendwie" angepackt wird.

Welche konkrete Verbindung besteht zwischen der Unit uMainWerk.pas und der "zu früh" initialisierten?

Wird da irgendwo im FormCreate schon was aufgerufen?

Mein Vorgehen wäre hier folgendes:

Es muss eine Loggingfunktion (in 'ner Komponente oder so), die in jede projektspezifische Unit eingebunden wird, vorhanden sein.

Alle Loggingeinträge "landen" in einer Datei.
Vor dem Schreiben des Eintrages wird die Datei geöffnet, nach dem Schreiben geschlossen.

In jeder projektspezifischen Unit wird im Initialisierungsbereich eine Zeile mit einem Zeitstempel und dem Namen der Unit in die Logdatei geschrieben.

Hiermit sollte anschließend die Aufrufreihenfolge der projektspezifischen Units "schriftlich" vorliegen.

Mit diesem Wissen kann man dann etwas gezielter die Abhängigkeiten durchforsten.

Prinzipiell hat man ja in jeder Unit zwei Uses-Blöcke, einen im Interface, den zweiten unter Implementation.

Alle Units, die nicht bereits zwingend im Interface bekannt sein müssen, da dort noch keine Abhängigkeiten bestehen, gehören in die Uses-Anweisung unter Implementation, auch dann, wenn Delphi sie automatisch in die Uses-Anweisung des Interface gepackt hat.

Soweit ich das mitbekommen habe, scheint dies auch Auswirkungen auf die Reihenfolge des Ladens der Units zu haben, lasse mich aber gerne eines Besseren belehren, da ich mich mit den Interna nun wirklich nicht so besonders auskenne.

frankyboy1974 8. Jan 2016 11:12

AW: Applikation initialisert "falsche" Unit
 
hallo,

also wenn ich das Problem richtig verstehe, kann ich als Programmierer keine Aussage darüber treffen, in welcher Reihenfolge die initalizion Blöcke ausgeführt werden. Sobald ich eine Unit (auch über drei Ecke einbinde) wird der Block irgendwann ausgeführt. Wenn ich aber keine Aussage treffen kann, wann der Block aufgerufen wird, muss ich diesen Block eben so programmieren, dass es immer funktioniert.

mfg

ZOD 8. Jan 2016 11:46

AW: Applikation initialisert "falsche" Unit
 
Danke für die Antworten bisher.

Während der Zuführung von Eiweisen und Kohlehydraten habe ich folgenden Gedanken entwickelt:

Was wäre, wenn die initialization von uMain schon immer aufgerufen wurden, nur dass bisher einfach die Funktion
Delphi-Quellcode:
JclStartExceptionTracking
True zurückgeliefert hat?
Dann wäre die Frage - was passiert in
Delphi-Quellcode:
JclStartExceptionTracking
oder besser: was fehlt, damit dort ein True als Rückgabewert gemeldet wird?

nahpets 8. Jan 2016 12:08

AW: Applikation initialisert "falsche" Unit
 
Das kannst Du hier jcl\source\windows\JclDebug.pas nachschauen.


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:34 Uhr.
Seite 1 von 3  1 23      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz