AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

SMART-Werte lesen

Ein Thema von ManuMF · begonnen am 23. Jun 2006 · letzter Beitrag vom 31. Jul 2007
Antwort Antwort
Seite 5 von 6   « Erste     345 6      
Benutzerbild von ManuMF
ManuMF

Registriert seit: 11. Jul 2005
1.016 Beiträge
 
Delphi 6 Personal
 
#41

Re: SMART-Werte lesen

  Alt 14. Jul 2006, 11:45
Hallo,

der Code von Muetze1 funktioniert ja, aber inzwischen gab es in dieser Experten-Diskussion (da kann ich nicht mithalten ) doch ein paar Verbesserungen.

Gibt es nun für den Code ein Update, oder kann man die alte Version gefahrlos nutzen?

Gruß,
ManuMF
Gruß,
ManuMF

Endlich hab ich was Positives an Vista entdeckt: Das mitgelieferte Mahjongg
  Mit Zitat antworten Zitat
Muetze1
(Gast)

n/a Beiträge
 
#42

Re: SMART-Werte lesen

  Alt 14. Jul 2006, 11:57
Gib's morgen, heute abend bin ich gesellschaftlich verpflichtet. Ansonsten will ich noch einen anderen Ansatz über das SPTI Interface machen, welches dann auch S-ATA mit bedienen kann.
  Mit Zitat antworten Zitat
Benutzerbild von ManuMF
ManuMF

Registriert seit: 11. Jul 2005
1.016 Beiträge
 
Delphi 6 Personal
 
#43

Re: SMART-Werte lesen

  Alt 14. Jul 2006, 14:11
Hallo,

cool, danke!

Ich habe aber noch mal eine Frage zu folgendem Code (mit der Ergänzung von Daniel G):
Delphi-Quellcode:
AOut.Add(Format('%.2X %-29s%d%20s%d%20s%d', [lDA^.bAttrID,
                                             coAttrNames[lAttr],
                                             lDA^.bAttrValue,
                                             ' ',
                                             lAT^.bWarrantyThreshold,
                                             ' ',
                                             lDA^.bRAWValue[0]]));
Ich möchte mir das ganze so umschreiben, dass ich später die Werte einzeln auslesen kann (aus einer StringList). Dazu verwende ich dann aus der CodeLibrary eine Funktion, um einen String anhand eines Trennzeichens zu zerlegen. Jetzt ist meine Frage: Wie muss der Format-String lauten, um statt der Leerzeichen jeweils einmal das Trennzeichen auszugeben? Mit Format habe ich immer noch so meine Probleme.

Gruß,
ManuMF
Gruß,
ManuMF

Endlich hab ich was Positives an Vista entdeckt: Das mitgelieferte Mahjongg
  Mit Zitat antworten Zitat
Muetze1
(Gast)

n/a Beiträge
 
#44

Re: SMART-Werte lesen

  Alt 14. Jul 2006, 14:18
Warum legst du dir nicht lieber einen Record oder ein Objekt an, welches jeweils ein Attribut mit allen Merkmalen hält. Dieses kannst du zu dem jeweiligen Eintrag in z.B. einer ListBox mit vermerken. Ich halte grundlegend und absolut nix davon, binäre Daten in einen String zu verfrachten um dann das ganze wieder zurück zu den Daten zu verwandeln. Das ist doch Humbuck - Du hast die Daten ordentlich vorliegen, also nimm diese und nutze sie weiter.
  Mit Zitat antworten Zitat
Benutzerbild von ManuMF
ManuMF

Registriert seit: 11. Jul 2005
1.016 Beiträge
 
Delphi 6 Personal
 
#45

Re: SMART-Werte lesen

  Alt 14. Jul 2006, 14:27
Hallo,

mir sind die Umwege schon klar, aber ich möchte es so machen, damit ich deinen Code nicht komplett umschreiben muss. Außerdem soll das Programm für mich sein, und wenn ich mir die falschen Daten auslese, werde ich mich selbst für Schäden wohl kaum belangen, falls es darum geht

Also noch einmal, mit großem "Bitte" :
Wie lautet der Format-String?

Gruß,
ManuMF
Gruß,
ManuMF

Endlich hab ich was Positives an Vista entdeckt: Das mitgelieferte Mahjongg
  Mit Zitat antworten Zitat
Muetze1
(Gast)

n/a Beiträge
 
#46

Re: SMART-Werte lesen

  Alt 14. Jul 2006, 14:39
Zitat von ManuMF:
mir sind die Umwege schon klar, aber ich möchte es so machen, damit ich deinen Code nicht komplett umschreiben muss. Außerdem soll das Programm für mich sein, und wenn ich mir die falschen Daten auslese, werde ich mich selbst für Schäden wohl kaum belangen, falls es darum geht
Gerade wenn es ein privates Projekt ist: Lern zu programmieren und das in einer effektiven, ordentlichen Weise. Wenn du dir das jetzt so angewöhnst, dann wirste nachher im Beruf lange brauchen um dir diese schlechten Angewohnheit wieder abzugewöhnen. Vor allem wirst du feststellen, dass es leichter ist mit Erweiterungen, etc.

Und warum nicht meinen Code umstellen? Irgendein plausibler Grund, warum man das nicht umschreiben sollte?

Delphi-Quellcode:
Format('%d;%s;%d;%d;%d', [lDA^.bAttrID,
                          coAttrNames[lAttr],
                          lDA^.bAttrValue,
                          lAT^.bWarrantyThreshold,
                          lDA^.bRAWValue[0]]);
Und das sind nicht alle Werte die in der Struktur pro Attribut zurück geliefert werden sondern nur ein Auszug. Ich hoffe das ist dir bewusst.
  Mit Zitat antworten Zitat
Benutzerbild von ManuMF
ManuMF

Registriert seit: 11. Jul 2005
1.016 Beiträge
 
Delphi 6 Personal
 
#47

Re: SMART-Werte lesen

  Alt 14. Jul 2006, 14:50
Hallo,

ja, es ist mir bewusst. Ich prüfe dann anhand z.B. SpeedFan, welche Werte ich nehme, etwa bei der Temperatur der RAW-Wert.

Es ging mir eigentlich nur darum, dass ich u.a. gesehen hatte, dass die ID nicht direkt als Dezimalzahl formatiert wird, deshalb war ich ein wenig verwirrt. Habe es jetzt so gelöst (mit #7 als Trennzeichen):
Delphi-Quellcode:
AOut.Add(Format('%.2x%s%s%s%d%s%d%s%d', [lDA^.bAttrID,
                                         #7,
                                         coAttrNames[lAttr],
                                         #7,
                                         lDA^.bAttrValue,
                                         #7,
                                         lAT^.bWarrantyThreshold,
                                         #7,
                                         lDA^.bRAWValue[0]]));
Gruß,
ManuMF
Gruß,
ManuMF

Endlich hab ich was Positives an Vista entdeckt: Das mitgelieferte Mahjongg
  Mit Zitat antworten Zitat
Muetze1
(Gast)

n/a Beiträge
 
#48

Re: SMART-Werte lesen

  Alt 14. Jul 2006, 15:02
Warum nicht einfach die #7 in den String anstatt ihn einfügen zu lassen?

Sprich: '%.2x'#7'%s'#7'%d'#7'%d'#7'%d' ?

Ansonsten ein Crashkurs:

%.2x%s%s%s%d%s%d%s%d ergibt folgende Teile:

1. %.2x
2.-4. %s
5. %d
6. %s
7. %d
8. %s
9. %d

2., 3., 4., 6. und 8. sind jeweils ein Platzhalter für eine Zeichenkette die eingefügt wird.
5., 7., 9. sind Platzhalter für einen ganzzahligen Wert, welcher dezimal ausgegegebn wird und ein Vorkomma haben kann.
1. ist ein Platzhalter für einen ganzzahligen Wert, welcher in hexadezimaler Schreibweise ausgegeben wird (das x besagt die hexadezimale Schreibweise in kleinen Buchstaben, ein grosses X würde grosse Buchstaben erzeugen) und der Punkt gibt an, dass du die Breite der Ausgabe festlegen willst. Die 2 nach dem Punkt sagt dann, dass die Ausgabe immer 2 Zeichen breit sein soll. Wenn nun also die hexadezimale Zahl kürzer ist, wird mit Leerzeichen vor der Zahl aufgefüllt. Hättest du die Leerzeichen lieber nach der Zahl, dann hättest du -2 schreiben sollen. Hättest du anstatt Leerzeichen lieber Nullen vor der Zahl, dann hättest du 02 schreiben müssen.
  Mit Zitat antworten Zitat
Benutzerbild von ManuMF
ManuMF

Registriert seit: 11. Jul 2005
1.016 Beiträge
 
Delphi 6 Personal
 
#49

Re: SMART-Werte lesen

  Alt 14. Jul 2006, 15:09
Hallo,

danke, jetzt wird mir einiges klar.

Und stimmt, ich hätte die Zeichen direkt reinschreiben können, warum auch immer ich es nicht habe
Gruß,
ManuMF

Endlich hab ich was Positives an Vista entdeckt: Das mitgelieferte Mahjongg
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#50

Re: SMART-Werte lesen

  Alt 14. Jul 2006, 20:21
Ihr geht doch jetzt auch mehrere \\.\PhysicalDriveX durch?

Hab jetzt mal bemerkt, daß meine IDE-Platte doch S.M.A.R.T kann (obwohl das BIOS immernoch sagt dort sei es "disabled"

Die Demo aus Beitrag #4 fand ja auch nur die S-ATA-Platte, aber jetzt werden alle Plättchen entdeckt

Ist also (wie ich grad bemerkte) das selbe "Problem" wie in Beitrag #20 (denk ich mal).

Code:
//WinNT+

unit Mainform;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

Uses
  SmartIOCTL,
  SmartFunc;

Procedure TForm1.Button1Click(Sender: TObject);
Var
  lSmartHandle: THandle;
  lError: LongWord;
  lVersionInfo: TGetVersionOutParams;
  [color=red]i, i2: Integer;[/color]
  lBytesReturned: LongWord;
  lSuccess: Boolean;

  bIDCmd,
  bDfpDriveMap : Byte;

  lInCmd: TSendCmdInParams;
  lOutCmd: TSendCmdOutParams;
  lIdOutCmd: Pointer;
  lAttrOutCmd: Pointer;
  lThreshOutCmd: Pointer;
Begin
  Memo1.Lines.Clear;

  [color=red]For i2 := 0 to 0 Do
  Begin[/color]

    bDfpDriveMap := 0;

    [color=red]lSmartHandle := CreateFile(PChar('\\.\PhysicalDrive' + IntToStr(i2)), GENERIC_READ Or GENERIC_WRITE,
      FILE_SHARE_READ Or FILE_SHARE_WRITE, Nil, OPEN_EXISTING, 0, 0);[/color]

    If ( lSmartHandle = INVALID_HANDLE_VALUE ) Then
    Begin
      lError := GetLastError;

      If ( Win32Platform = VER_PLATFORM_WIN32_WINDOWS ) Then
        Memo1.Lines.Add(format('Unable to open SMARTVSD, error code: 0x%.08X (%s)', [lError, SysErrorMessage(lError)]))
      Else If ( Win32Platform = VER_PLATFORM_WIN32_NT ) Then
        Memo1.Lines.Add(format('Unable to open physical drive, error code: 0x%.08X (%s)', [lError, SysErrorMessage(lError)]));

      Exit;
    End
    Else
      Memo1.Lines.Add('SMART interface opened...');

    GetMem(lIdOutCmd,    SizeOf(TSendCmdOutParams) + IDENTIFY_BUFFER_SIZE - 1);
    GetMem(lAttrOutCmd,  SizeOf(TSendCmdOutParams) + READ_ATTRIBUTE_BUFFER_SIZE - 1);
    GetMem(lThreshOutCmd, SizeOf(TSendCmdOutParams) + READ_THRESHOLD_BUFFER_SIZE - 1);
    Try
      If GetSMARTVersionInfo(lSmartHandle, lVersionInfo) Then
      Begin
        Memo1.Lines.Add('DFP_GET_VERSION returned:');
        Memo1.Lines.Add(format('   bVersion       = %d',    [lVersionInfo.bVersion]));
        Memo1.Lines.Add(format('   bRevision      = %d',    [lVersionInfo.bRevision]));
        Memo1.Lines.Add(format('   fCapabilities  = 0x%.08x', [lVersionInfo.fCapabilities]));
        Memo1.Lines.Add(format('   bReserved      = 0x%x',  [lVersionInfo.bReserved]));
        Memo1.Lines.Add(format('   bIDEDeviceMap  = 0x%x',  [lVersionInfo.bIDEDeviceMap]));
    //    Memo1.Lines.Add(format('   cbBytesReturned = %d', cbBytesReturned);
      End
      Else
        Memo1.Lines.Add('DFP_GET_VERSION failed.');


      For i := 0 To MAX_IDE_DRIVES Do
      Begin

        //
        // If there is a IDE device at number "i" issue commands
        // to the device.
        //
        If ( ( ( lVersionInfo.bIDEDeviceMap Shr i ) And 1 ) <> 0 ) Then
        Begin

          //
          // Try to enable SMART so we can tell if a drive supports it.
          // Ignore ATAPI devices.
          //

          If ( ( ( lVersionInfo.bIDEDeviceMap Shr i ) And $10 ) = 0 ) Then
          Begin
            FillChar(lInCmd, SizeOf(lInCmd), 0);
            FillChar(lOutCmd, SizeOf(lOutCmd), 0);

            If ( DoEnableSMART(lSmartHandle,
                               @lInCmd,
                               @lOutCmd,
                               i,
                               lBytesReturned) ) Then
            Begin
              Memo1.Lines.Add(format('SMART enabled on drive: %d', [i]));

              //
              // Mark the drive as SMART enabled
              //
              bDfpDriveMap := bDfpDriveMap Or ( 1 Shl i );
            End
            Else
            Begin
              Memo1.Lines.Add(format('SMART Enable Command Failed, Drive: %d', [i]));
              Memo1.Lines.Add(format(' DriverStatus: bDriverError=0x%.08X, bIDEStatus=0x%.08X',
                                     [ lOutCmd.DriverStatus.bDriverError,
                                       lOutCmd.DriverStatus.bIDEStatus]));
            End;

            Memo1.Lines.Add(Format('    cbBytesReturned: %d', [lBytesReturned]));
          End;

          //
          // Now, get the ID sector for all IDE devices in the system.
          // If the device is ATAPI use the IDE_ATAPI_ID command,
          // otherwise use the IDE_ID_FUNCTION command.
          //
          If ( ( ( lVersionInfo.bIDEDeviceMap Shr i ) And $10 ) <> 0 ) Then
            bIDCmd := IDE_ATAPI_ID
          Else
            bIDCmd := IDE_ID_FUNCTION;

          FillChar(lInCmd, SizeOf(lInCmd), 0);
          FillChar(lIdOutCmd^, SizeOf(TSendCmdOutParams) + IDENTIFY_BUFFER_SIZE - 1, 0);

          If ( DoIDENTIFY(lSmartHandle,
                          @lInCmd,
                          lIdOutCmd,
                          bIDCmd,
                          i,
                          lBytesReturned) ) Then
          Begin
            DisplayIdInfo(Memo1.Lines, PIDSector(@(PSendCmdOutParams(lIdOutCmd)^.bBuffer[0]))^,
                          lInCmd, bIDCmd, bDfpDriveMap, i);
          End
          Else
          Begin
            Memo1.Lines.Add(format('Identify Command Failed on Drive: %d', [i]));
            Memo1.Lines.Add(format(' DriverStatus: bDriverError=0x%.08X, bIDEStatus=0x%.08X',
                                   [ PSendCmdOutParams(lIdOutCmd)^.DriverStatus.bDriverError,
                                     PSendCmdOutParams(lIdOutCmd)^.DriverStatus.bIDEStatus]));
          End;

          Memo1.Lines.Add(format('  cbBytesReturned: %d', [lBytesReturned]));
        End;
      End;

      //
      // Loop through all possible IDE drives and send commands to the ones that support SMART.
      //
      For i := 0 To MAX_IDE_DRIVES Do
      Begin
        If ( ( ( bDfpDriveMap Shr i ) And 1 ) <> 0 ) Then
        Begin
          FillChar(lAttrOutCmd^,  SizeOf(TSendCmdOutParams) + READ_ATTRIBUTE_BUFFER_SIZE - 1, 0);
          FillChar(lThreshOutCmd^, SizeOf(TSendCmdOutParams) + READ_THRESHOLD_BUFFER_SIZE - 1, 0);

          lSuccess := DoReadAttributesCmd( lSmartHandle,
                                           @lInCmd,
                                           lAttrOutCmd,
                                           i);
          If ( Not lSuccess ) Then
          Begin
            Memo1.Lines.Add(format('SMART Read Attr Command Failed on Drive: %d', [i]));
            Memo1.Lines.Add(format(' DriverStatus: bDriverError=0x%.08X, bIDEStatus=0x%.08X',
                                   [ PSendCmdOutParams(lAttrOutCmd)^.DriverStatus.bDriverError,
                                     PSendCmdOutParams(lAttrOutCmd)^.DriverStatus.bIDEStatus]));
          End

            // ReadAttributes worked. Try ReadThresholds.
          Else If ( Not DoReadThresholdsCmd( lSmartHandle,
                                             @lInCmd,
                                             lThreshOutCmd,
                                             i) ) Then
          Begin
            lSuccess := False;

            Memo1.Lines.Add(format('SMART Read Thresh Command Failed on Drive: %d', [i]));
            Memo1.Lines.Add(format(' DriverStatus: bDriverError=0x%.08X, bIDEStatus=0x%.08X',
                                   [ PSendCmdOutParams(lAttrOutCmd)^.DriverStatus.bDriverError,
                                     PSendCmdOutParams(lAttrOutCmd)^.DriverStatus.bIDEStatus]));
          End
          Else
            lSuccess := True;

          //
          // The following report will print if ReadAttributes works.
          // If ReadThresholds works, the report will also show values for
          // Threshold values.
          //
          If ( lSuccess ) Then
          Begin
            DoPrintData( Memo1.Lines,
                         @(PSendCmdOutParams(lAttrOutCmd)^.bBuffer[0]),
                         @(PSendCmdOutParams(lThreshOutCmd)^.bBuffer[0]),
                         i);
          End;
        End;
      End;

    Finally
      [color=red]CloseHandle(lSmartHandle);[/color]
      FreeMem(lIdOutCmd);
      FreeMem(lAttrOutCmd);
      FreeMem(lThreshOutCmd);
    End;

    [color=red]Memo1.Lines.Add('');
    Memo1.Lines.Add('');
    Memo1.Lines.Add('');

  End;[/color]

End;

End.

[add]
und ihr seid euch sicher, daß
Zitat:
Bit 0 = Primary Master
Bit 1 = Primary Slave
Bit 2 = Secondary Master
Bit 3 = Secondary Slave
stimmt?

meine S-ATA is ja an Port 0 und in der Systemsteuerung steht sie als "Primary Master",
aber SMARTAppD7 meint "SMART enabled on drive: 2" ... also "Secondary Master"

Bei der IDE stimmt die 0.
Code:
SMART interface opened...
DFP_GET_VERSION returned:
    bVersion       = 1
    bRevision      = 1
    fCapabilities  = 0x00000007
    bReserved      = 0x0
    bIDEDeviceMap  = 0x4
SMART enabled on drive: 2
     cbBytesReturned: 16
Drive 2 is an IDE Hard drive that supports SMART
    #Cylinders: 16383, #Heads: 16, #Sectors per Track: 63
     IDE TASK FILE REGISTERS:
          bFeaturesReg    = 0x0
          bSectorCountReg = 0x1
          bSectorNumberReg = 0x1
          bCylLowReg      = 0x0
          bCylHighReg     = 0x0
          bDriveHeadReg   = 0xA0
          Status          = 0xEC
     Model number: Maxtor 6V250F0                         
     Firmware rev: VA11163
     Serial number: V594J4NG          
   cbBytesReturned: 528
Data for Drive Number 2
Attribute Structure Revision         Threshold Structure Revision
             32                                      32
   -Attribute Name-      -Attribute Value-     -Threshold Value-
03 Spin Up Time                191                    63
04 Start/Stop Count            253                    0
05 Reallocated Sector Count    253                    63
07 Seek Error Rate             253                    0
08 Seek Time Performance       249                    187
09 Power On Hours Count        253                    0
0A Spin Retry Count            253                    157
0B Calibration Retry Count     253                    223
0C Power Cycle Count           253                    0
BD (Unknown attribute)         100                    0
BE (Unknown attribute)         50                    0
C0 (Unknown attribute)         253                    0
C1 (Unknown attribute)         253                    0
C2 (Unknown attribute)         48                    0
C3 (Unknown attribute)         253                    0
C4 (Unknown attribute)         253                    0
C5 (Unknown attribute)         253                    0
C6 (Unknown attribute)         253                    0
C7 (Unknown attribute)         199                    0
C8 (Unknown attribute)         253                    0
C9 (Unknown attribute)         253                    0
CA (Unknown attribute)         253                    0
CB (Unknown attribute)         253                    180
CC (Unknown attribute)         253                    0
CD (Unknown attribute)         253                    0
CF (Unknown attribute)         253                    0
D0 (Unknown attribute)         253                    0
D2 (Unknown attribute)         253                    0
D3 (Unknown attribute)         253                    0
D4 (Unknown attribute)         253                    0



SMART interface opened...
DFP_GET_VERSION returned:
    bVersion       = 1
    bRevision      = 1
    fCapabilities  = 0x00000007
    bReserved      = 0x0
    bIDEDeviceMap  = 0x1
SMART enabled on drive: 0
     cbBytesReturned: 16
Drive 0 is an IDE Hard drive that supports SMART
    #Cylinders: 16383, #Heads: 16, #Sectors per Track: 63
     IDE TASK FILE REGISTERS:
          bFeaturesReg    = 0x0
          bSectorCountReg = 0x1
          bSectorNumberReg = 0x1
          bCylLowReg      = 0x0
          bCylHighReg     = 0x0
          bDriveHeadReg   = 0xA0
          Status          = 0xEC
     Model number: MAXTOR 4K040H2                         
     Firmware rev: A08.150B
     Serial number: 572123615033       
   cbBytesReturned: 528
Data for Drive Number 0
Attribute Structure Revision         Threshold Structure Revision
             11                                      11
   -Attribute Name-      -Attribute Value-     -Threshold Value-
01 Raw Read Error Rate         100                    20
03 Spin Up Time                87                    20
04 Start/Stop Count            96                    8
05 Reallocated Sector Count    98                    20
07 Seek Error Rate             100                    23
09 Power On Hours Count        72                    1
0A Spin Retry Count            100                    0
0B Calibration Retry Count     100                    20
0C Power Cycle Count           96                    8
0D (Unknown attribute)         100                    23
C2 (Unknown attribute)         85                    42
C3 (Unknown attribute)         100                    0
C4 (Unknown attribute)         100                    20
C5 (Unknown attribute)         100                    20
C6 (Unknown attribute)         100                    0
C7 (Unknown attribute)         126                    0



Unable to open physical drive, error code: 0x00000002 (Das System kann die angegebene Datei nicht finden)
$2B or not $2B
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 5 von 6   « Erste     345 6      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 16:49 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 by Thomas Breitkreuz