Die Klasse „TChromium“ bietet das Setzen Methodenzeiger für eine Reihe von Ereignissen an. Die auf diesem Weg eingebundenen Methoden werden nicht auf dem Hauptthread ausgeführt, sondern auf einem nebenläufigen Thread. Dabei ist „TChromium“ nicht von „TThread“ abgeleitet und es bietet sich nicht die Synchronisierung auf den Hauptthread mittels „TThread.Synchronize“ an. Lange Rede kurzer Sinn, ich habe eine funktionierende Lösung mittels einer Erweiterung von „TTimer“ durch eine Hilfsklasse. Ich stelle die Lösung hier in Skizzenform zur Diskussion vor. Es kann sein, dass es noch eine einfachere Lösung gibt, deren ich mir nicht bewusst bin. Suchanfragen mittels globaler Suchmaschinen haben aber gezeigt, dass das Problem nicht generell befriedigend behandelt wird.
Synchronisiert werden soll der Methodenzeiger „OnDownLoadUpdated“ einer „TChromium“-Instanz. Wenn die zugeordnete Methode aufgerufen wird, dann ist im Anweisungsblock die Bedingung
GetCurrentThreadId <> MainThreadId
erfüllt. So kann man programmiert nachfragen, ob eine Routine in einem nebenläufigen Thread ausgeführt wird. Man kann auch in Ansicht->Debug-Fenster->Threads nachsehen. Der erste gelistete Thread ist der Hauptthread und der aktive ist markiert.
Innerhalb von „OnDownLoadUpdated“ lässt sich abfragen, ob der Download abgeschlossen worden ist:
Delphi-Quellcode:
downloadInProgress := downloadItem.IsInProgress and
(downloadItem.FullPath <> '');
Die Feldvariable „downloadInProgress“ ist false, wenn der Download abgeschlossen ist. Diese Feldvariable wird auch von „OnCanClose“ des Hauptformulars verwendet.
Wenn der Download abgeschlossen ist, dann soll ein Panel mit einer Schaltfläche zum Anzeigen der Datei über „ShellExecute“ sichtbar gemacht werden und dabei der Dateiname auf der Schaltfläche angezeigt werden. Dies muss auf dem Hauptthread erfolgen. Damit der entsprechende Code auf dem Hauptthread ausgeführt werden kann, wird ein „Timer“-Objekt auf unkonventionelle Weise verwendet. Es ist so, dass die dem Feld „OnTimer“ zugeordnete Methode garantiert auf dem Hauptthread ausgeführt wird. Diese kann dann selbst den Timer über die Eigenschaft „Enabled“ ausschalten, sodass die Methode „OnTimer“ exakt einmal und nicht öfter ausgeführt wird. Ferner ist eine Objektsperre mittels „System.TMonitor.Enter“ notwendig, um entsprechende Felder aus dem Hauptformular, dem die „OnTimer“-Methode zugeordnet ist, für die Parameterübergabe an die „OnTimer“-Methode zu reservieren.
Erweiterung der Klasse „TTimer“ mittels der Hilfsklasse „TThreadTimer“:
Delphi-Quellcode:
TThreadTimer = class helper for TTimer
procedure enterNotification;
procedure runNotification(notification:TNotifyEvent);
procedure exitNotification;
class procedure notificationFinished(Sender:TObject);
end;
Implementation:
Delphi-Quellcode:
procedure TThreadTimer.enterNotification;
begin
System.TMonitor.Enter(self); //Objektsperre.
repeat until Enabled=false;
//warten auf nicht abgeschlossene „OnTimer“-Methode
end;
procedure TThreadTimer.runNotification(notification:TNotifyEvent);
begin
OnTimer := notification; //Methodenzeiger setzen
Enabled := true; //Methodenaufruf per Timer zulassen
end;
procedure TThreadTimer.exitNotification;
begin
System.TMonitor.Exit(self); //Objektsperre aufheben
end;
class procedure TThreadTimer.notificationFinished(Sender:TObject);
begin
TTimer(Sender).Enabled := false;
//„OnTimer“ hat Ausführung abgeschlossen und hebt Sperre auf
end;
Die verwendete „TTimer“-Instanz heißt „tmrResize“. Im Designer ist „Enabled“ auf false gesetzt und das „OnTimer“-Ereignis bleibt dort undefiniert. Es wird dann per Code gesetzt. Der Synchronisierungscode in der nebenläufigen Methode des Ereignisses „OnDownLoadUpdated“:
Delphi-Quellcode:
if not downloadInProgress and (downloadItem.FullPath <> '') then begin
//Download ist abgeschlossen und eine Zieldatei ist festgelegt
try
tmrResize.enterNotification;
//Reservierung der „TTimer“-Instanz „tmrResize“ und des ...
//Zugriffs auf die Feldvariablen für die Parameterübergabe.
latestDownload := copy(downloadItem.FullPath, 1,
Length(downloadItem.FullPath));
//Parameter für das „OnTimer“-Ereignis setzen
tmrResize.runNotification(showPnlDownloadFeature);
//„showPnlDownloadFeature“ als „OnTimer“-Ereignis verwenden.
finally
tmrResize.exitNotification;
//Freigabe des TTimerobjektes. Die Freigabe der Sperre mittels
//„Enabled“ in der „TTimer“-Instanz erfolgt dann im „OnTimer“-
//Ereignis
end;
end;
Der Code von „showPnlDownloadFeature“, der auf dem Hauptthread ausgeführt wird, sieht dann wie folgt aus:
Delphi-Quellcode:
try
pnlDownloadFeature.Visible := true;
//Panel mit Option Dateiladen sichtbar machen
btnDownloadFeature.Caption := ExtractFileName(latestDownload) + ' laden';
//Dateinamen als Schaltfeldbeschriftung verwenden.
Application.ProcessMessages;
//Bildschirm aktualsieren
Chrome.SetFocus(true);
//Fokus auf Browser belassen
finally
TTimer.notificationFinished(Sender);
//abschließend wird die „TTimer“-Instanz deaktiviert und mittels
//„Enabled“=false die Freigabe signalisiert. Diese Methode wird
//mit diesen hier beschriebenen Zugriffen exakt einmal aufgerufen.
end;