AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Programmieren allgemein Delphi Sind alle Thread Variablen in OnTerminate threadsicher?
Thema durchsuchen
Ansicht
Themen-Optionen

Sind alle Thread Variablen in OnTerminate threadsicher?

Ein Thema von berens · begonnen am 24. Feb 2025 · letzter Beitrag vom 24. Feb 2025
Antwort Antwort
berens

Registriert seit: 3. Sep 2004
441 Beiträge
 
Delphi 10.4 Sydney
 
#1

Sind alle Thread Variablen in OnTerminate threadsicher?

  Alt 24. Feb 2025, 13:55
Hallo,
ich stehe mal wieder von einem sehr abstrakten Problem.

Mein Thread liest mir in Execute mehrere Werte aus und speicherte diese in den integer Variablen MeinThread.X und MeinThread.Y. Der Thread ist dann fertig und löst "OnTerminate" aus.

Hier ein handgestricktes Beispiel:
Delphi-Quellcode:
procedure TForm1.MeinThreadTerminated(Sender: TObject);
var
  t: TMeinThread;
begin
  if not assigned(Sender) then Exit;

  // Sicherstellen, dass der Sender wirklich unser erwarteter Thread ist
  if not (Sender is TMeinThread) then Exit;

  // Lokale Variable für sicheren Zugriff auf die Thread-Daten
  t := TMeinThread(Sender);

  ShowMessage(inttostr(t.x));
end;
Laut Delphi-Hilfe ist "OnTerminate" Threadsicher. Das bedeutet zunächst (nur), dass es im VCL/Hauptthread des Programms ausgeführt wird, und ich auf's GUI zugreifen darf, so wie hier mit ShowMessage etc.

Es sagt mir jedoch nicht, ob ich "sicher" auf Thread-Interne Variablen zugreifen darf.

In einem alten Thema hier im Forum -ich finde den Link gerade nicht, es ist viele Jahre her- ging es darum, dass ich bei einem Thread außerhalb von "Synchronize" niemals auf Thread-Variablen zugreifen darf, die in Execute verwendet werden. Selbst ein einzelne Boolean-Variable, die nur von .Execute verändert wird und vom VCL-Thread nur ausgelesen werden soll (bl = True = "Berechnung dauert an") soll, ist -so die damalige Aussage- "nicht threadsicher", weil Gott-weiß-was-passieren-kann, wenn der Thread gerade das Bit schreibt, und der VCL-Thread das Bit gleichzeitig ausliest.

Was "Gott-weiß-was" denn überhaupt sein könnte, kann ich mir bis heute nicht erklären, aber seit dem hat sich bei mir eine sehr intensive Furcht vor abstrakten AccessViolations, dem plötzlichen ungültig-werden von Variablen und Objekten und was-nicht-alles etabliert, incl. abruptem Programmende und Bluescreen, was alles passieren kann, wenn ich dieses eine Bit aus dem Hauptthread lese, nachdem es irgendwann mal im Thread gesetzt wurde.

Versteht mich nicht falsch:
Ich verstehe die Notwendigkeit von Synchronisieren, CriticalSections, Race-Conditions und Niemals-auf-GUI-Elemente-zugreifen mittlerweile sehr gut.
Es geht hier um dieses Abtrakte "Greif NIEMALS von außen auf Thread-Variabeln zu!", hier konkret: AUCH nach Thread Ende!

Bei OnTerminate ist der Thread FERTIG. Es kann (meiner Meinung nach) zu keinen "Race Conditions" etc. mehr kommen. Wenn ich die Variablen X und Y des Threads garantiert niemals anfasse außer in .Execute und OnTerminate - da dürfte doch nichts passieren, oder?

Im Gegensatz dazu habe ich in einem anderen Programmteil einen anderen Thread, der mehrere Statusinformationen hat, die fortlaufend im GUI aktualisiert werden sollen. Da muss ich natürlich(?) alle Variablen einmal "intern" (private) für den Thread haben, in den die Infos alle reinkommen, und in Synchronize(MeinSync) weise ich dann die Werte alle an die "public"-Variablen zu, so nach dem Motto
Delphi-Quellcode:
procedure TMeinThread.MeinSync;
begin
  StatusX := FStatusX;
  StatusY := FStatusY;
  //etc.
end;
Muss ich jetzt für ein threadsicheres "OnTerminate" in Realfall all meine 20 Variablen von einer "private" in eine "public" mit Synchronize kopieren, oder kann ich da bedenkenlos drauf zugreifen?


ChatGPT sagt dazu:
Kann man in OnTerminate Threadsicher auf alle Variablen des Threads zugreifen?

Code:
ChatGPT 4o:
Nein, OnTerminate garantiert keine Thread-Sicherheit für den Zugriff auf die Variablen des Threads.

Warum?
OnTerminate läuft im Hauptthread, weil es von Synchronize aufgerufen wird.
Der eigentliche Execute-Code des Threads läuft im Hintergrund-Thread.
Falls der Hintergrund-Thread während des Zugriffs auf eine Variable noch arbeitet oder sie verändert, kann es zu Race Conditions oder Dateninkonsistenzen kommen.
Was ich für Quatsch halte, weil der Thread ja fertig ist, und die Variablen nicht mehr verändern kann(?).

o3-mini-high sagt dazu:
Code:
Ja – unter bestimmten Voraussetzungen. Das OnTerminate-Ereignis wird nämlich im Hauptthread (über Synchronize oder PostMessage) aufgerufen, nachdem die Execute-Methode vollständig abgeschlossen ist. Das bedeutet, dass der Thread selbst nicht mehr aktiv seine Instanzvariablen ändert und du daher in OnTerminate auf deren Werte zugreifen kannst.

Wichtig ist dabei:

Keine parallele Modifikation:
Stelle sicher, dass keine anderen Threads (oder asynchrone Prozesse) gleichzeitig auf dieselben Variablen zugreifen. Normalerweise werden die Instanzvariablen des Threads nur von diesem selbst modifiziert, sodass das nach Beendigung sicher ist.

Zeitpunkt:
OnTerminate wird ausgeführt, bevor der Thread-Objekt tatsächlich freigegeben wird (bei FreeOnTerminate = True). Innerhalb von OnTerminate ist der Thread also noch vollständig gültig.

Kurz gesagt:
Solange der Thread nur seine eigenen Variablen verändert hat und diese nicht von anderen Threads parallel modifiziert werden, kannst du in OnTerminate threadsicher auf alle Instanzvariablen zugreifen.
Das würde ich auch eher denken?
Delphi 10.4 32-Bit auf Windows 10 Pro 64-Bit, ehem. Delphi 2010 32-Bit auf Windows 10 Pro 64-Bit
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke
Online

Registriert seit: 10. Jun 2003
Ort: Berlin
9.845 Beiträge
 
Delphi 12 Athens
 
#2

AW: Sind alle Thread Variablen in OnTerminate threadsicher?

  Alt 24. Feb 2025, 14:07
Ich halte wenig davon, für diese Zwecke OnTerminate zu verwenden. Viel sinnvoller ist es, wenn man dafür eigene Ereignisse oder Callbacks definiert. Erstens hat man dann die Kontrolle darüber wann was aus dem Thread herausgereicht wird, zweitens ist dann alles sauber benannt, drittens kann kein Zugriff versehentlich im falschen Threadkontext erfolgen (wenn man von außen auf interne Werte zugreifen kann) und viertens stellen sich dann solche Fragen gar nicht (weil man es selbst explizit macht).

Zudem kann sich die interne Implementierung ändern, wenn ein bestimmtes Verhalten nicht explizit dokumentiert ist.
Sebastian Jänicke
AppCentral
  Mit Zitat antworten Zitat
berens

Registriert seit: 3. Sep 2004
441 Beiträge
 
Delphi 10.4 Sydney
 
#3

AW: Sind alle Thread Variablen in OnTerminate threadsicher?

  Alt 24. Feb 2025, 14:19
Okay, das wäre dann identisch mit dem Verfahren, das ich bisher verwende, um aus laufenden Threads Informationen auszulesen.

Würde dieses Start vereinfachte Beispiel deinen Erwartungen entsprechen, oder habe ich was wichtiges vergessen oder falsch gemacht?
Delphi-Quellcode:
type
  TBerechnungsThread = class(TThread)
  private
    InternX: Integer; // Interne Variable für die Berechnung
    procedure SyncX; // Synchronisiert InternX mit ExternX
  protected
    procedure Execute; override;
  public
    ExternX: Integer; // Externe Variable (thread-sicher über Synchronize)
    OnBerechnungFertig: TNotifyEvent;
    constructor Create;
  end;

constructor TBerechnungsThread.Create;
begin
  inherited Create(True); // Thread startet pausiert (Suspended=True)
  FreeOnTerminate := True; // Automatisch nach Beendigung freigeben
  OnBerechnungFertig := NIL;
  InternX := 0;
  SyncX; // auch ExtenX korrekt initialisieren, Synchronize brauchen wir hier noch nicht(?)
end;

procedure TBerechnungsThread.SyncX;
begin
  FExternX := InternX; // Sicherer Transfer in den Hauptthread
end;

procedure TBerechnungsThread.Execute;
begin
  // Beispielhafte Berechnung (hier einfach nur Zuweisung)
  InternX := Random(1000); // Zufallszahl als Beispiel

  // Synchronisiere den Wert von InternX zu ExternX
  Synchronize(SyncX);

  // Falls ein Ereignis registriert ist, rufe es im Hauptthread auf
  if Assigned(OnBerechnungFertig) then
    Synchronize(procedure begin OnBerechnungFertig(Self); end);
end;


// Hauptprogramm:
procedure TForm1.BerechnungAbgeschlossen(Sender: TObject);
begin
  ShowMessage('Berechnung abgeschlossen! ExternX = ' + IntToStr(TBerechnungsThread(Sender).ExternX));
end;

procedure TForm1.StartThread;
var
  Thread: TBerechnungsThread;
begin
  Thread := TBerechnungsThread.Create;
  Thread.OnBerechnungFertig := BerechnungAbgeschlossen; // Ereignis setzen
  Thread.Start; // Thread starten
end;
Delphi 10.4 32-Bit auf Windows 10 Pro 64-Bit, ehem. Delphi 2010 32-Bit auf Windows 10 Pro 64-Bit
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke
Online

Registriert seit: 10. Jun 2003
Ort: Berlin
9.845 Beiträge
 
Delphi 12 Athens
 
#4

AW: Sind alle Thread Variablen in OnTerminate threadsicher?

  Alt 24. Feb 2025, 15:13
Du machst dir das Leben unnötig schwer, indem du mit TNotifyEvent als Typ arbeitest.

Du kannst den Wert einfach in das Event direkt als Parameter hineinschreiben. Dann musst du nichts zwischenspeichern und brauchst nach außen auch keine Property.

Ich kann das noch als Beispiel schreiben. Vorher aber eine Frage:
Wie kompliziert ist die Berechnung eigentlich? Brauchst du wirklich eine eigene Klasse dafür oder hast du das genommen, weil du TThread nur als abgeleitete Klasse kennst? Sprich sind das nur z.B. 20 Zeilen oder ist das mehr?
Sebastian Jänicke
AppCentral
  Mit Zitat antworten Zitat
berens

Registriert seit: 3. Sep 2004
441 Beiträge
 
Delphi 10.4 Sydney
 
#5

AW: Sind alle Thread Variablen in OnTerminate threadsicher?

  Alt 24. Feb 2025, 16:13
Achja, das wäre natürlich auch eine einfachere Variant die Werte zurückzugeben. Ich nehme an, du meinst es beispielhaft so?
Delphi-Quellcode:
type
  // Definition des Events mit allen relevanten Parametern
  TTestCompleteEvent = procedure(Sender: TObject; const ResultText: string; AllesOk: Boolean;
                                 ScreenWidth, ScreenHeight: Integer;
                                 ScreenDimensionMismatch, Vollbild: Boolean;
                                 CurrentMonitor: Integer) of object;

  TTestThread = class(TThread)
  private
    FOnTestComplete: TTestCompleteEvent;
    FResultText: string;
    FAllesOk: Boolean;
    procedure DoNotifyTestComplete;
  protected
    procedure InitializeVariabels; virtual;
    procedure Execute; override;
  public
    constructor Create;
    property OnTestComplete: TTestCompleteEvent read FOnTestComplete write FOnTestComplete;
  end;

procedure TTestThread.DoNotifyTestComplete;
begin
  if Assigned(FOnTestComplete) then
// Hier mit Dummy-Werten
    FOnTestComplete(Self, FResultText, FAllesOk, 123, 456, False, True, 1);
end;

Im realen Programm für den Endkunden habe ich mehrere "Selbsttests", die beim Programmstart laufen sollen. Thematisch haben diese Selbsttests relativ wenig miteinander zu tun: Der eine holt den freien Festplattenspeicher, der andere überprüft in der Datenbank-Tabelle, ob ungültige Zeitbezüge vorhanden sind, der nächste ob in der Registry unerwartete Werte stehen etc.

Aktuell läuft alles nacheinander, was sich im Laufe der Zeit immer weiter "aufschaukelt", und -je nach Netzwerkgeschwindigkeit- beim Programmstart zwischen 1-2 Sekunden bis zu mehreren Minuten brauchen kann, wenn der Kunde das Programm über eine Bambus-VPN-Leitung startet. Das kann so nicht weitergehen. Ich muss diese ganzen Tests in Threads packen. Das Programm kann/darf auch mit ungültigen Werten starten, die der Selbsttest eigentlich erst finden/beheben soll - das ist kein Problem, denn auch wenn sie beim Programmstart nach dem alten Verfahren hintereinander laufen, wird der Benutzer nur benachrichtigt.

Die Idee ist also, dass alle Tests erzeugt werden, die Testroutine wird über den Thread laufen gelassen (Threadsicherheit bei Datenbankabfragen kommt nun hinzu, CoInitialize etc.!), und sobald fertig wird im GUI beim entsprechenden Menüpunkt ein grüner Haken oder ein rotes X angezeigt. Die Tests selbst müssen aber (je nach Test) Werte aus dem Thread anzeigen können, damit ich dem Benutzer auch zeigen kann, "was genau" nicht simmt.

Wenn z.B. die Bildschirmauflösung sich von dem Wert in der Datenbank unterscheidet (es gibt eine Bedingung, wann das relevant ist), kommt eine Info an den Benutzer. Er kann nun die Auflösung in der Datenbank auf diesen (oder einen anderen) Wert ändern (neue Auflösung in die Datenbank schreiben; hat nix mit Thread zu tun), und den Test erneut laufen lassen. Da der Thread eh auf die DB zugreift, sagt er halt nicht nur "Auflösung richtig: Ja/Nein", sondern gibt mir auch die Auflösung, damit ich sie dem Benutzer im GUI anzeigen lassen kann, ohne sie *nochmals* auslesen zu lassen. Nach dem Ändern der Werte läuft der Thread erneut (Selbsttest), und beim OnTestComplete werden dann die Werte in den Edit-Feldern wieder aktualisiert.

Damit kann erstmal das Programm starten, die Haken für "Ok" oder die X für "Warnung/Fehler" ploppen dann im Menü so nach und nach bei den einzelnen Punkten davon als Grafik auf. Zumindest reagiert das Program in dieser Zeit auf Benutzereingaben.

---

Da mit Threads sehr viel Unglücke passieren können, die man beim Entwickeln nicht unmittelbar sieht (Arbeiten mit TAdoConnection ohne CoInitialize klappt meistens im DebugModus, und ohne Debugger in 70% der Fälle, aber nicht immer - für diesen und ähnliche Fehler benötigt man Erfahrung und Hintergrundwissen, was ich -in Bezug auf Threads- nicht soooo sicher habe, dass ich mich da jetzt munter in Gefilden bewegen Wollen würde, die dann weitere Probleme verursachen.

> Brauchst du wirklich eine eigene Klasse dafür oder hast du das genommen, weil du TThread nur als abgeleitete Klasse kennst?
Ich nehme an, du redest hier von anonymen Methoden/Threads? ChatGPT schlägt das hier vor, und das würde das Thema ja um den Faktor 100 vereinfachen:
Delphi-Quellcode:
TThread.CreateAnonymousThread(
  procedure
  var
    ResultText: string;
    AllesOk: Boolean;
    ScreenWidth, ScreenHeight: Integer;
    ScreenDimensionMismatch, Vollbild: Boolean;
    CurrentMonitor: Integer;
  begin
    // Beispielhafte Berechnung
    ScreenWidth := 1920;
    ScreenHeight := 1080;
    Vollbild := True;
    CurrentMonitor := 1;
    ScreenDimensionMismatch := (ScreenWidth <> Screen.Width) or (ScreenHeight <> Screen.Height);
    AllesOk := True;
    ResultText := 'Test abgeschlossen!';

    // Synchronisieren mit Hauptthread und direkt Callback aufrufen
    TThread.Synchronize(nil,
      procedure
      begin
        TestAbgeschlossen(nil, ResultText, AllesOk, ScreenWidth, ScreenHeight,
                          ScreenDimensionMismatch, Vollbild, CurrentMonitor);
      end);
  end
).Start;
Wäre das in etwa auch dein Vorschlag?
Delphi 10.4 32-Bit auf Windows 10 Pro 64-Bit, ehem. Delphi 2010 32-Bit auf Windows 10 Pro 64-Bit
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke
Online

Registriert seit: 10. Jun 2003
Ort: Berlin
9.845 Beiträge
 
Delphi 12 Athens
 
#6

AW: Sind alle Thread Variablen in OnTerminate threadsicher?

  Alt 24. Feb 2025, 17:51
Ja, genau. Bei (bezogen auf den Code) kurzen Threadoperationen würde ich immer anonyme Threads (oder TTask oder ähnliches) nutzen. Die kann man irgendwo verpacken, aber da würde ich nicht extra eine Klasse ableiten. Das lenkt nur vom Wesentlichen ab.

Und auch bei deiner Methode DoNotifyTestComplete würde ich TThread.Synchronize mit einer anonymen Methode nutzen.

Und wenn du mehrere Aufrufe benötigst, kannst du TThread.Queue verwenden, so dass der Threads weiterläuft, während der Aufruf an den Hauptthread geht.

Aufpassen musst du, wenn du ganze Objekte auf diese Weise übergeben möchtest, da das ja nur Referenzen sind. Dann bietet es sich an, der Klasse ein Interface zu spendieren, damit dein Objekt lange genug lebt, aber dennoch freigegeben wird.
Sebastian Jänicke
AppCentral

Geändert von jaenicke (24. Feb 2025 um 18:09 Uhr)
  Mit Zitat antworten Zitat
berens

Registriert seit: 3. Sep 2004
441 Beiträge
 
Delphi 10.4 Sydney
 
#7

AW: Sind alle Thread Variablen in OnTerminate threadsicher?

  Alt 24. Feb 2025, 22:31
Danke schonmal für den guten Input.

TTask kannte ich tatsächlich bisher nicht.

Im Prinzip kann ich nun einfach meine alten Testprozeduren (fast!) 1:1 übernehmen, indem ich sie einfach mit TTask.Run umgebe.
Klar, auch hier muss ich wieder auf die Threadsicherheit achten, aber der ganze Overhead entfällt tatsächlich komplett.

Delphi-Quellcode:
TTask.Run(
  procedure
  var
    Wert: Integer;
  begin
    // Siehe Hinweis 1, hier im Beispiel ignorieren!!!
    SelfRef := Self; // Sicherstellen, dass wir auf die Instanz zugreifen können

    Wert := Random(100);
    TThread.Queue(nil,
      procedure
      begin
        ShowMessage('Ergebnis: ' + IntToStr(Wert));
        // Konkret in meinem Beispiel:
        Self.ExternerWert := Wert;
      end);
  end);
Nur noch eine Frage: ChatGPT hat (wahrscheinlich nicht ganz zu Unrecht) recht große Bedenken, dass "Self" bis zum Ende des Tasks nicht mehr existiert. Hier in dem Beispiel unwahrscheinlich, in der Praxis mit der Bambusleitung über VPN nicht zu 100% ausschließbar, dass der Benutzer das Programm wieder schließt, bevor der TTask beendet ist. ChatGPT will hier unbedingt Self in "SelfRef" zwischenspeichern, und dann auch unten SelfREF.ExternerWert := Wert; verwenden. Meiner Meinung nach rettet dies das Programm auch nicht mehr vor dem Absturz, wenn ich im Hauptprogramm schon die dazugehörige Komponente mit "FreeAndNil" freigegeben habe. Was tun? Einfach ExternerWert := Wert; (ohne Self oder SelfREF) zuweisen und das Beste hoffen, oder akuter Handlungsbedarf?

"Zur Güte" habe ich nun noch versucht, die Zuweisungen an Self über eine Callback-Prozedur zu lösen, damit ich nicht vom TTask aus (trotz TThread.Queue!) direkt auf die Komponenten-Variablen zugreifen muss. Unnötig? Dringend erforderlich?

Ich bin auch sehr unglücklich, dass ich kein "Early Exit" mehr verwenden kann. Schließlich muss auch, wenn alle Vorbedingungen fehlschlagen, am Schluss der Wert ans Hauptprogramm mit TThread.Queue übergeben werden. Ich fände das jetzt relativ blöd und schwer zu warten bei jedem nicht bestandenen "if" mit " TestComplete(False, FUserName, FDomain, FVollbild); Exit;" das zu jeder Anweisung zu schreiben. Wäre aber wahrscheinlich dennoch sinnvoller als dieser Quatsch mit dem zwischengespeicherten Status "bl", der erst ganz am Schluss an "blOkay" übergeben wird.

Delphi-Quellcode:
procedure TSelbsthilfe_AutoLogon.Test;
const
  P = 'Test';
begin
  try
    TTask.Run(procedure
    var
      bl, blOkay: Boolean;
      tmpRegistry: TRegistry;
      FVollbild: Boolean;
      FResultText: string;
      FUserName, FDomain: string;
    begin
      tmpRegistry := NIL;
      try
        FUserName := '';
        FDomain := '';
        FResultText := '';

        // Vollbildmodus auslesen
        FVollbild := HAL_Registry_GetBool('blFullScreen', False);
        if FVollbild then begin
          blOkay := False;

          tmpRegistry := TRegistry.Create(KEY_ALL_ACCESS OR KEY_WOW64_64KEY);
          try
            tmpRegistry.RootKey := HKEY_LOCAL_MACHINE;

            bl := tmpRegistry.OpenKeyReadOnly('Software\Microsoft\Windows NT\CurrentVersion\Winlogon');
            if not bl then begin
              FResultText := _('Fehlgeschlagen: Status der automatische Anmeldung konnte ausgelesen werden.');
            end;

            // AutoLogon-Option auslesen
            if bl and (not tmpRegistry.ValueExists('AutoAdminLogon')) then begin
              FResultText := _('Fehlgeschlagen: Automatische Anmeldung nicht aktiv: Wert existiert nicht!');
              bl := False;
            end;

            if bl and (tmpRegistry.ReadString('AutoAdminLogon') <> '1') then begin
              FResultText := _('Fehlgeschlagen: Automatische Anmeldung nicht aktiv: Wert nicht wie erwartet!');
              bl := False;
            end;

            // AutoLogon-Benutzername auslesen
            if bl and (not tmpRegistry.ValueExists('DefaultUserName')) then begin
              FResultText := _('Fehlgeschlagen: Automatische Anmeldung nicht aktiv: Benutzername existiert nicht!');
              bl := False;
            end;

            if bl then begin
              FUserName := trim(tmpRegistry.ReadString('DefaultUserName'));
              if FUserName = 'then begin
                FResultText := _('Fehlgeschlagen: Automatische Anmeldung nicht aktiv: Benutzername ist leer!');
                bl := False;
              end;
            end;

            // AutoLogon-Domain auslesen
            if bl and (not tmpRegistry.ValueExists('DefaultDomainName')) then begin
              FResultText := _('Fehlgeschlagen: Automatische Anmeldung nicht aktiv: Domain existiert nicht!');
              bl := False;
            end;

            if bl then begin
              FDomain := tmpRegistry.ReadString('DefaultDomainName');
              if FDomain = 'then begin
                FResultText := _('Fehlgeschlagen: Automatische Anmeldung nicht aktiv: Domain ist leer!');
                bl := False;
              end;
            end;

            blOkay := bl;
            tmpRegistry.CloseKey;
          finally
            FreeAndNil(tmpRegistry);
          end;

        end else begin
          blOkay := True;
          FResultText := _('Erfolg: Dieser PC startet den Viewer nur im Vorschaumodus. Der Status der automatischen Windows-Anmeldung wurde nicht überprüft.');
        end;

        TThread.Queue(nil,
          procedure
          begin
            TestComplete(blOkay, FUserName, FDomain, FVollbild);
          end);
      except
        on E: Exception do begin
          TThread.Queue(nil,
            procedure
            begin
              FResultText := Format(_('Fehlgeschlagen: Fehler beim Auslesen der automatischen Anmeldung: "%s".'), [E.Message]);
              TestComplete(False, FUserName, FDomain, FVollbild);
            end);
        end;
      end;
    end);
  except
    on E: Sysutils.Exception do begin
      LogP(QualifiedClassName, P, '', E.Message, ws_SEVERITY_EXCEPTION);
    end;
  end;
end;

procedure TSelbsthilfe_AutoLogon.TestComplete(_AllesOK: Boolean; _Username, _Domain: string; _Vollbild: Boolean);
begin
  FAllesOk := _AllesOK;
  UserName := _Username;
  Domain := _Domain;
  Vollbild := _Vollbild;
  NotifyTestCompleted;
  if _Vollbild then begin
    Self.MyFrame.edtUserName.Text := _UserName;
    Self.MyFrame.edtDomain.Text := _Domain;
  end;
end;
PS: blOkay wird erst gegen Ende der Prozedur gesetzt, damit nicht das "bl=True" bei einer Exception als Rückgabewert gilt (blOkay wird also nur dann gesetzt, wenn alle Befehle ohne Exception ausgeführt werden, ansonsten bleibt es bei blOkay=False als Initialisierungswert). Ist wie ich gerade sehe unnötig, weil ich bei except eh bei FAllesOkay ein statisches "False" als Parameter zum Testobjekt zurückgebe. Naja, egal.


Meine wichtigsten Fragen nochmal zusammengefasst:
1) TThread.Queue( ... Self.ExternerWert := Wert; ...) - Problematisch? Ja oder Nein?
2) Zusätzliche Referenz auf Self? Quatsch oder Notwendig?
3) TThread.Queue( ... TestComplete(False, FUserName, FDomain, FVollbild); ... ) NOTWENDIG oder DARF ich auch direkt innerhalb von TTask.Run
Delphi-Quellcode:
  TThread.Queue(nil,
  procedure
  begin
    FAllesOk := blOkay;
    UserName := FUsername;
    Domain := FDomain;
    Vollbild := FVollbild;
    NotifyTestCompleted;
  end;
  end);
Delphi 10.4 32-Bit auf Windows 10 Pro 64-Bit, ehem. Delphi 2010 32-Bit auf Windows 10 Pro 64-Bit
  Mit Zitat antworten Zitat
Antwort Antwort


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 06:22 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