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