|
![]() |
|
Registriert seit: 12. Jun 2008 186 Beiträge Delphi 12 Athens |
#1
Himitsu hat natürlich Recht!
Das .Free habe ich erst spontan, nachträglich, im Forum nachgesetzt, weil mir der Befehl zu fehlen schien und ich befürchtete, als Erstes eins deswegen auf den Deckel zu bekommen. Nun ist es umgekehrt ![]() Hier der korrekte Aufruf:
Code:
Meine ursprüngliche Frage ist damit aber nicht beantwortet.
procedure TForm1.Button1Click(Sender: TObject);
var Downloader: TFileDownloader; mstream: TMemoryStream; begin mstream := TMemoryStream.Create; Downloader := TFileDownloader.Create('https://www.example/datei.txt', mstream); Downloader.OnDownloadComplete := DownloadComplete; Downloader.Start; end; Hier die (lauffähige) Unit:
Code:
Gibt es dazu Anmerkungen?
unit SynchronousHttpClientStreamDownloader;
interface uses System.Classes, System.SysUtils, System.Net.HttpClient, System.Net.URLClient; type TDownloadCompleteEvent = procedure(const stream: TStream; Success: Boolean) of object; TFileDownloader = class(TThread) private FURL: string; FStream: TStream; FHttpClient: THTTPClient; FOnDownloadComplete: TDownloadCompleteEvent; FSuccess: Boolean; procedure DownloadFile; procedure DoDownloadComplete; protected procedure Execute; override; public constructor Create(const URL: String; aStream: TStream); destructor Destroy; override; property OnDownloadComplete: TDownloadCompleteEvent read FOnDownloadComplete write FOnDownloadComplete; end; implementation { TFileDownloader } constructor TFileDownloader.Create(const URL: String; aStream: TStream); begin inherited Create(True); FreeOnTerminate := True; FURL := URL; FStream := aStream; FHttpClient := THTTPClient.Create; end; destructor TFileDownloader.Destroy; begin FHttpClient.Free; inherited; end; procedure TFileDownloader.DownloadFile; var vHTTP: THTTPClient; begin try FStream := TMemorystream.Create; // FStream muss an dieser Stelle erzeugt werden, im Execute klappt es nicht. Free muss aber im Execute stehen Assert(FStream <> nil); vHTTP := THTTPClient.Create; vHTTP.CustomHeaders['Pragma'] := 'no-cache'; try vHTTP.Get(FURL, FStream); FSuccess := True; finally vHTTP.Free; end; except on E: Exception do begin FSuccess := False; end; end; end; procedure TFileDownloader.DoDownloadComplete; begin if Assigned(FOnDownloadComplete) then begin FOnDownloadComplete(FStream, FSuccess); FStream.Free end end; procedure TFileDownloader.Execute; begin DownloadFile; Synchronize(DoDownloadComplete); end; end.
Steffen
Geändert von Stewag (17. Jul 2024 um 09:35 Uhr) |
![]() |
Registriert seit: 17. Sep 2006 Ort: Barchfeld 27.666 Beiträge Delphi 12 Athens |
#2
Du gibst den Stream nur frei, wenn dem Ereignis ein Handler zugewiesen wurde?
Detlef
"Ich habe Angst vor dem Tag, an dem die Technologie unsere menschlichen Interaktionen übertrumpft. Die Welt wird eine Generation von Idioten bekommen." (Albert Einstein) Dieser Tag ist längst gekommen |
![]() |
Registriert seit: 11. Okt 2003 Ort: Elbflorenz 44.538 Beiträge Delphi 12 Athens |
#3
Warum den THTTPClient doppelt erstellen?
Im Excecute ist es OK, das im Create war eh ungenutzt. Assert(FStream <> nil); Kann niemals NIL sein, denn wenn "doch", dann wirft das Create bereits eine Exception und es kommt dort nie vorbei. FOnDownloadComplete erst beim Ausführen zu prüfen ist auch etwas unpraktisch. Alles war ja sinnlos, wenn diese Methode fehlt, da der Download nur als Stream und von diesem Event verwendet wird. Ohne das, wurde es vollkommen nutzlos runtergeladen. Also hier am Besten gleich zu Beginn prüfen und z.B. eine Exception werfen, bereits im Create.
Delphi-Quellcode:
Für die Funktion zwar nicht relevant, aber
constructor TFileDownloader.Create(const URL: String; OnDownloadComplete: TDownloadCompleteEvent);
begin if not Assigned(OnDownloadComplete) then raise Exception.Create('peng'); inherited Create(False); // und jetzt außen kein Start mehr, da es am Ende des Create von selst startet FreeOnTerminate := True; FURL := URL; FOnDownloadComplete := OnDownloadComplete; // und der Rest im Execute/DownloadFile end; TDownloadCompleteEvent = reference to procedure(Stream: TStream; Success: Boolean); hat den Vorteil, dass man hier nicht nur Methoden und KlassenMethoden übergegen kann, sondern auch einfache Prozeduren oder anonyme Methoden. Den Stream intern erstellen, da wo er auch freigegeben wird, nicht extern. Nja, da FHttpClient, FSuccess und eigentlich auch FStream im Grunde ausschließlich während der Laufzeit des Threads nötig sind und bezüglich einem threadsaven Zugriffs nicht von außen abgreifbar sein sollten, sind sie als lokale Variable im Execute eh besser aufgehoben. Das Event als Property, auf welches man auch nach dem Start extern zugreifen kann, fliegt auch raus. Sowas lässt sich wundeschön als Startparameter übergeben, also als Parameter ans Create und das Start aka Suspended=False ebenfalls. Die restlichen Variablen/Felder müssen nicht unbedingt global sein. Durch das FreeOnTerminate sind die Thread-Variable und seine Property von Extern sowieso nicht mehr zu verwenden, da sie per se als ungültig zu betrachten sind, denn von extern weißt du nicht, ob der Thread schon fertig ist und freigegeben wurde. Selbst wenn der Thread blieb, dann dort als ClassProcedure rein, aber jetzt einfach mal blöd als einface Prozedur.
Delphi-Quellcode:
Und weil jetzt vom der Threadableitung im Grunde nichts mehr übrig ist, lass ich des nachfolgend einfach mal weg.
TFileDownloader = class(TThread)
private FURL: string; FStream: TStream; FOnDownloadComplete: TDownloadCompleteEvent; procedure DownloadFile; procedure DoDownloadComplete; protected procedure Execute; override; public class procedure Download(const URL: String; OnDownloadComplete: TDownloadCompleteEvent); static; end;
Delphi-Quellcode:
Gut, dass man im OnDownloadComplete zwar mitbekommt, ob es nicht ging, aber nicht warum ... statt eines doofen Success könnte man z.B. die Exception oder ihre Message übergeben.
uses
System.RTLConsts, System.SysUtils, System.Classes, System.Net.HttpClient, System.Net.URLClient; type TDownloadCompleteEvent = reference to procedure(Stream: TStream; Success: Boolean); procedure ThreadedDownloadFile(const URL: String; OnDownloadComplete: TDownloadCompleteEvent); begin if (URL = '') or not Assigned(OnDownloadComplete) then raise EArgumentException.CreateRes(@SArgumentNil); TThread.CreateAnonymousThread( procedure var HTTP: THTTPClient; Stream: TMemorystream; Success: Boolean; begin TThread.NameThreadForDebugging('ThreadedDownloadFile'); HTTP := THTTPClient.Create; Stream := TMemoryStream.Create; try HTTP.CustomHeaders['Pragma'] := 'no-cache'; try HTTP.Get(URL, Stream); Success := True; except Success := False; end; TThread.Synchronize(nil, procedure begin OnDownloadComplete(Stream, Success); end); finally Stream.Free; HTTP.Free; end; end).Start; end; ThreadedDownloadFile('http://sonst.wo/dat.ei', YourDownloadComplete); // oder ThreadedDownloadFile('http://sonst.wo/dat.ei', procedure(const URL: String; Stream: TStream; Success: Boolean) begin if Success then Machwas(Stream); end); Für mehrere Downloads das selbe OnDownloadComplete, dann auch die URL mit als Parameter dort rein, damit man dort weiß was es ist.
Delphi-Quellcode:
uses
System.RTLConsts, System.SysUtils, System.Classes, System.Net.HttpClient, System.Net.URLClient; type TDownloadCompleteEvent = reference to procedure(const URL: String; Stream: TStream; Error: Exception); procedure ThreadedDownloadFile(const URL: String; OnDownloadComplete: TDownloadCompleteEvent); begin if (URL = '') or not Assigned(OnDownloadComplete) then raise EArgumentException.CreateRes(@SArgumentNil); TThread.CreateAnonymousThread( procedure var HTTP: THTTPClient; Stream: TMemorystream; Success: Boolean; DError: Exception; begin TThread.NameThreadForDebugging('ThreadedDownloadFile'); DError := nil; HTTP := THTTPClient.Create; Stream := TMemoryStream.Create; try HTTP.CustomHeaders['Pragma'] := 'no-cache'; try HTTP.Get(URL, Stream); {TThread.Synchronize(nil, procedure begin OnDownloadComplete(URL, Stream, nil); end);} except {on E: Exception do begin TempErr := E; // durch einen Bug muß es kopiert werden, auch wenn der Compiler sich bei OnDownloadComplete(URL, nil, E); nicht beschwert ... E ist im Sync leider NIL TThread.Synchronize(nil, procedure begin //OnDownloadComplete(URL, nil, E); // siehe Bugreport im alten QualityPotal OnDownloadComplete(URL, nil, TempErr); end); end;} FreeAndNil(Stream); // für das if-Assigned im OnDownloadComplete ... oder einfach lassen, auch wenn es eh nichts sinnvolles enthält, und nur auf Assigned(Error) prüfen DError := AcquireExceptionObject as Exception; end; TThread.Synchronize(nil, procedure begin OnDownloadComplete(URL, Stream, DError); end); finally DError.Free; Stream.Free; HTTP.Free; end; end).Start; end; ThreadedDownloadFile('http://sonst.wo/dat.ei', YourDownloadComplete); // oder ThreadedDownloadFile('http://sonst.wo/dat.ei', procedure(const URL: String; Stream: TStream; Error: Exception) begin if Assigned(Stream) then begin // if not Assigned(Error) then Machwas(Stream); ShowMessage('Download complete: ' + URL); end; end);
Ein Therapeut entspricht 1024 Gigapeut.
Geändert von himitsu (17. Jul 2024 um 10:43 Uhr) |
![]() |
Registriert seit: 12. Jun 2008 186 Beiträge Delphi 12 Athens |
#4
Danke für deine Hinweise und Code, Himitsu!
Da ich mit threads aber kaum Erfahrung habe, bekomme ich damit leider keine funktionierende SyncThreadedDownload Unit hin. Verstehe ich das richtig, dass weder Constructor noch Destructor dafür benötigt werden? Dies läuft bei mir auch nicht:
Code:
Vor allem aber verstehe ich nicht, wie/wo deine Prozedur ThreadedDownloadFile einzubinden ist.
TDownloadCompleteEvent = reference to procedure(Stream: TStream; Success: Boolean);
Eigentlich sollte doch eine Unit, die eine Datei in einem synchronen Thread lädt, ein übliches Mittel in jedem Werkzeugkasten sein. Deshalb wundert es mich, dass ich auch nach intensivem googeln nichts dergleichen finde. Könnte hier vielleicht jemand netterweise seine entsprechende Unit (Firemonkey!) zur Verfügung stellen? Da niemand etwas anderes erwähnt hat, ist es wohl erforderlich, dass nach dem Aufruf des Download Threads ein Timer zyklisch prüft, ob OnDownladComplete schon ein Ergebnis bereit gestellt hat?
Steffen
|
![]() |
Registriert seit: 25. Apr 2008 Ort: Neunkirchen 793 Beiträge |
#5
Da niemand etwas anderes erwähnt hat, ist es wohl erforderlich, dass nach dem Aufruf des Download Threads ein Timer zyklisch prüft, ob OnDownladComplete schon ein Ergebnis bereit gestellt hat?
|
![]() |
Registriert seit: 11. Okt 2003 Ort: Elbflorenz 44.538 Beiträge Delphi 12 Athens |
#6
Es gibt Fremd-Bibliotheken, die sowas drin haben.
Das Ganze kann man ja noch mit vielen Dingen erweitern und somit nicht allen Recht machen ... den Einen isses zuviel und Anderen fehlt immer irgendwas. * z.B. ein Pooling, damit man 200 Dateien downloaden kann, aber nicht zu viele Threads gleichzeitig laufen. * schönere Fehlernehandlung * Proxy * uvm. Je nach Anforderung muß man sich dann was Passendes suchen. Es gibt verschiedene Arten von Prozeduren/Methoden und genauso gibt es verschiedene Arten von MethodenZeigern, welche Referenzen auf diese Methoden aufnehmen können.
Delphi-Quellcode:
type
TEvent1 = reference to procedure(const URL: String; Stream: TStream; Error: Exception); // Proc1, Proc2, Proc3, Proc4 und Inline5 TEvent2 = procedure(const URL: String; Stream: TStream; Error: Exception) of object; // Proc2, Proc3 TEvent3 = procedure(const URL: String; Stream: TStream; Error: Exception); // Proc1 (glaub auch Proc4) // Prozedur procedure Proc1(const URL: String; Stream: TStream; Error: Exception); type TDemo = class // Methode procedure Proc2(const URL: String; Stream: TStream; Error: Exception); // KlassenMethode class procedure Proc3(const URL: String; Stream: TStream; Error: Exception); // statische KlassenMethode class procedure Proc4(const URL: String; Stream: TStream; Error: Exception); static; end; // anonyme Methode, also "Inline5" im Code begin eineVariable := procedure(const URL: String; Stream: TStream; Error: Exception) begin end; AlsParameter(procedure(const URL: String; Stream: TStream; Error: Exception) begin end); end; // eingebettete Prozedur procedure {TDemo.}Test; procedure Proc6(const URL: String; Stream: TStream; Error: Exception); begin end; begin Proc6(…); end; Freigeben muß man den Thread ja nicht selbst, denn durch das FreeOnTerminate gibt er sich selbst frei. Weil man extern nicht weiß wann, darf extern grundsätzlich niemals "ungeschützt" auf eine Variable zugegriffen werden, welche auf diesen Thread zeigt. Extern garkeine Variable, mit dem Objektzeiger, und schon kommt auch niemand auf dumme Ideen. Ohne FreeOnTerminate könnte man extern z.B. auf Thread.Terminated prüfen und anschließend selbst den den Thread freigeben, bzw. vorher z.B. die runtergeladenen Daten von ihm abholen. Man kann selbst TThread ableiten und seinen Code ins Execute einfügen, sowie eventuell auch ins Create. Im Prnzip gibt es aber bereits eine fertige Ableitung des TThread, die eine Prozedur bekommt, welche sie dann im Thread ausführt. ![]()
Delphi-Quellcode:
Also anstatt einer eigenen TThread-Klasse, hatte ich einfach nur dieses Fertige benutzt.
type
TAnonymousThread = class(TThread) private FProc: TProc; protected procedure Execute; override; public constructor Create(const AProc: TProc); end; class function TThread.CreateAnonymousThread(const ThreadProc: TProc): TThread; begin Result := TAnonymousThread.Create(ThreadProc); end; constructor TAnonymousThread.Create(const AProc: TProc); begin inherited Create(True); FreeOnTerminate := True; FProc := AProc; end; procedure TAnonymousThread.Execute; begin FProc(); end;
Ohne den auskommentierten Code, für eine alternative Behandlung, ist es hier recht kurz gehalten (passt alles komplett auf den Bildschirm), und durch die anonymen (eingebetteten) Methoden liegt hier der Code funktional übersichtlich in seiner Ablaufreihenfolge. Aber grundsätzlich ist es auch kein Problem Vieles in externe Methoden auszulagern, aber dann am Besten schön in einer Klasse gekapselt. Bei dem eingebetteten Synchronize kann man es auch ausnutzen, dass sich Variablen "automatisch" vom Übergeordneten Code dort reingeben lässt. (Delphi verschiebt diese Variablen "heimlich" in ein Interface, mit Referenzzähung ... bis der aufrufende Code und der Eingebetette alle beendet wurden)
Ein Therapeut entspricht 1024 Gigapeut.
Geändert von himitsu (19. Jul 2024 um 18:40 Uhr) |
![]() |
Registriert seit: 12. Jun 2008 186 Beiträge Delphi 12 Athens |
#7
![]() Genau an der Stelle (OnDownloadComplete) weißt du doch, das du fertig bist. Also warum noch ein Timer?
![]() Himitsu ist sehr klug und verdient sicher seine Brötchen mit Software. Ich bin Hobbyprogrammierer, verstehe leider nur Bahnhof und habe kein Problem damit auch Code einzusetzen, den ich nicht bis ins Letzte durchdrungen habe. ![]()
Steffen
Geändert von Stewag (19. Jul 2024 um 21:47 Uhr) |
![]() |
Registriert seit: 10. Jun 2003 Ort: Berlin 10.054 Beiträge Delphi 12 Athens |
#8
Da ich mit threads aber kaum Erfahrung habe, bekomme ich damit leider keine funktionierende SyncThreadedDownload Unit hin.
Wenn du den Code in eine separate Unit packen möchtest, musst du die Funktion natürlich unter interface deklarieren, damit sie aus anderen Units sichtbar ist:
Delphi-Quellcode:
Dann schreibst du die Unit SyncThreadedDownload dort, wo du sie nutzen möchtest, unter uses. Dann kannst du einfach das Beispiel von himitsu 1:1 verwenden:
unit SyncThreadedDownload;
interface uses System.RTLConsts, System.SysUtils, System.Classes, System.Net.HttpClient, System.Net.URLClient; type TDownloadCompleteEvent = reference to procedure(const URL: String; Stream: TStream; Error: Exception); procedure ThreadedDownloadFile(const URL: String; OnDownloadComplete: TDownloadCompleteEvent); implementation procedure ThreadedDownloadFile(const URL: String; OnDownloadComplete: TDownloadCompleteEvent); begin ... end; end.
Delphi-Quellcode:
Statt Machwas(Stream) musst du natürlich den heruntergeladenen Stream mit den Daten verwenden, um die Datei z.B. zu verarbeiten oder zu speichern. Und natürlich musst du auch die korrekte Adresse der Datei eintragen.
ThreadedDownloadFile('http://sonst.wo/dat.ei',
procedure(const URL: String; Stream: TStream; Error: Exception) begin if Assigned(Stream) then begin // if not Assigned(Error) then Machwas(Stream); ShowMessage('Download complete: ' + URL); end; end); |
![]() |
Registriert seit: 12. Jun 2008 186 Beiträge Delphi 12 Athens |
#9
Danke jaenicke, aber so einfach ist es nicht.
Es fehlt schon mal der Deklarationsteil und auch der Constructor. Habe mal versucht, das nach den Angaben von Himitsu zu ergänzen. So bekomme ich zumindest schon mal keinen Laufzeitfehler:
Code:
Leider klappt aber der Aufruf mit keiner der beiden angegebenen Methoden. Beide mal erhalte ich den Fehler "[dcc32 Fehler] Unit1.pas(43): E2076 Diese Form des Methodenaufrufs ist nur für Klassenmethoden oder Konstruktoren zulässig"
unit SyncThreadedDownload;
interface uses System.RTLConsts, System.SysUtils, System.Classes, System.Net.HttpClient, System.Net.URLClient; type TDownloadCompleteEvent = reference to procedure(const URL: String; Stream: TStream; Error: Exception); TFileDownloader = class(TThread) procedure ThreadedDownloadFile(const URL: String; OnDownloadComplete: TDownloadCompleteEvent); private FStream: TStream; FOnDownloadComplete: TDownloadCompleteEvent; public constructor Create(const URL: String; OnDownloadComplete: TDownloadCompleteEvent); property OnDownloadComplete: TDownloadCompleteEvent read FOnDownloadComplete write FOnDownloadComplete; end; implementation { TFileDownloader } constructor TFileDownloader.Create(const URL: String; OnDownloadComplete: TDownloadCompleteEvent); begin if not Assigned(OnDownloadComplete) then raise Exception.Create('peng'); inherited Create(False); // Himitsu: "und jetzt außen kein Start mehr, da es am Ende des Create von selbst startet" -> Äääh, wie bitte? FreeOnTerminate := True; FOnDownloadComplete := OnDownloadComplete; // Himitsu: "und der Rest im Execute/DownloadFile" -> ich glaube der Rest steckt jetzt in ThreadedDownloadFile, oder ??? end; procedure TFileDownloader.ThreadedDownloadFile(const URL: String; OnDownloadComplete: TDownloadCompleteEvent); begin if (URL = '') or not Assigned(OnDownloadComplete) then raise EArgumentException.CreateRes(@SArgumentNil); TThread.CreateAnonymousThread( procedure var HTTP: THTTPClient; Stream: TMemorystream; DError: Exception; begin TThread.NameThreadForDebugging('ThreadedDownloadFile'); DError := nil; HTTP := THTTPClient.Create; Stream := TMemorystream.Create; try HTTP.CustomHeaders['Pragma'] := 'no-cache'; try HTTP.Get(URL, Stream); { TThread.Synchronize(nil, procedure begin OnDownloadComplete(URL, Stream, nil); end); } except { on E: Exception do begin TempErr := E; // durch einen Bug muß es kopiert werden, auch wenn der Compiler sich bei OnDownloadComplete(URL, nil, E); nicht beschwert ... E ist im Sync leider NIL TThread.Synchronize(nil, procedure begin //OnDownloadComplete(URL, nil, E); // siehe Bugreport im alten QualityPotal OnDownloadComplete(URL, nil, TempErr); end); end; } FreeAndNil(Stream); // für das if-Assigned im OnDownloadComplete ... oder einfach lassen, auch wenn es eh nichts sinnvolles enthält, und nur auf Assigned(Error) prüfen DError := AcquireExceptionObject as Exception; end; TThread.Synchronize(nil, procedure begin OnDownloadComplete(URL, Stream, DError); end); finally DError.Free; Stream.Free; HTTP.Free; end; end).Start; end; end.
Code:
type
TForm1 = class(TForm) Button1: TButton; Memo1: TMemo; procedure Button1Click(Sender: TObject); procedure DownloadComplete(const URL: String; stream: TStream; Error: Exception); private end; var Form1: TForm1; implementation {$R *.fmx} procedure TForm1.Button1Click(Sender: TObject); begin {Methode 1: TFileDownloader.ThreadedDownloadFile('https://www.example.de/test', procedure(const URL: String; stream: TStream; Error: Exception) begin if Assigned(stream) then begin // if not Assigned(Error) then begin Memo1.lines.LoadFromStream(stream); ShowMessage('Download complete: ' + URL); end; end; end); } // Methode 2 TFileDownloader.ThreadedDownloadFile('https://www.example.de/test', DownloadComplete); end; procedure TForm1.DownloadComplete(const URL: String; stream: TStream; Error: Exception); begin //Wie kann ich hier abfragen, ob eine Exception vorliegt? Memo1.lines.LoadFromStream(stream) // else // Label1.Text := 'Download failed'; end;
Steffen
Geändert von Stewag (21. Jul 2024 um 18:18 Uhr) |
![]() |
Registriert seit: 10. Jun 2003 Ort: Berlin 10.054 Beiträge Delphi 12 Athens |
#10
Danke jaenicke, aber so einfach ist es nicht.
Delphi-Quellcode:
Wenn du es als Klasse nutzen möchtest, kannst du das natürlich tun (finde ich auch besser), aber dann brauchst du eine Klassenmethode:
unit SyncThreadedDownload;
interface uses System.RTLConsts, System.SysUtils, System.Classes, System.Net.HttpClient, System.Net.URLClient; type TDownloadCompleteEvent = reference to procedure(const URL: String; Stream: TStream; Error: Exception); procedure ThreadedDownloadFile(const URL: String; OnDownloadComplete: TDownloadCompleteEvent); implementation procedure ThreadedDownloadFile(const URL: String; OnDownloadComplete: TDownloadCompleteEvent); begin if (URL = '') or not Assigned(OnDownloadComplete) then raise EArgumentException.CreateRes(@SArgumentNil); TThread.CreateAnonymousThread( procedure var HTTP: THTTPClient; Stream: TMemorystream; Success: Boolean; DError: Exception; begin TThread.NameThreadForDebugging('ThreadedDownloadFile'); DError := nil; HTTP := THTTPClient.Create; Stream := TMemoryStream.Create; try HTTP.CustomHeaders['Pragma'] := 'no-cache'; try HTTP.Get(URL, Stream); {TThread.Synchronize(nil, procedure begin OnDownloadComplete(URL, Stream, nil); end);} except {on E: Exception do begin TempErr := E; // durch einen Bug muß es kopiert werden, auch wenn der Compiler sich bei OnDownloadComplete(URL, nil, E); nicht beschwert ... E ist im Sync leider NIL TThread.Synchronize(nil, procedure begin //OnDownloadComplete(URL, nil, E); // siehe Bugreport im alten QualityPotal OnDownloadComplete(URL, nil, TempErr); end); end;} FreeAndNil(Stream); // für das if-Assigned im OnDownloadComplete ... oder einfach lassen, auch wenn es eh nichts sinnvolles enthält, und nur auf Assigned(Error) prüfen DError := AcquireExceptionObject as Exception; end; TThread.Synchronize(nil, procedure begin OnDownloadComplete(URL, Stream, DError); end); finally DError.Free; Stream.Free; HTTP.Free; end; end).Start; end; end.
Delphi-Quellcode:
unit SyncThreadedDownload;
interface uses System.RTLConsts, System.SysUtils, System.Classes, System.Net.HttpClient, System.Net.URLClient; type TFileDownloader = class private type TDownloadCompleteEvent = reference to procedure(const URL: String; Stream: TStream; Error: Exception); public class procedure ThreadedDownloadFile(const URL: String; OnDownloadComplete: TDownloadCompleteEvent); end; implementation class procedure TFileDownloader.ThreadedDownloadFile(const URL: String; OnDownloadComplete: TDownloadCompleteEvent); begin if (URL = '') or not Assigned(OnDownloadComplete) then raise EArgumentException.CreateRes(@SArgumentNil); TThread.CreateAnonymousThread( procedure var HTTP: THTTPClient; Stream: TMemorystream; Success: Boolean; DError: Exception; begin TThread.NameThreadForDebugging('ThreadedDownloadFile'); DError := nil; HTTP := THTTPClient.Create; Stream := TMemoryStream.Create; try HTTP.CustomHeaders['Pragma'] := 'no-cache'; try HTTP.Get(URL, Stream); {TThread.Synchronize(nil, procedure begin OnDownloadComplete(URL, Stream, nil); end);} except {on E: Exception do begin TempErr := E; // durch einen Bug muß es kopiert werden, auch wenn der Compiler sich bei OnDownloadComplete(URL, nil, E); nicht beschwert ... E ist im Sync leider NIL TThread.Synchronize(nil, procedure begin //OnDownloadComplete(URL, nil, E); // siehe Bugreport im alten QualityPotal OnDownloadComplete(URL, nil, TempErr); end); end;} FreeAndNil(Stream); // für das if-Assigned im OnDownloadComplete ... oder einfach lassen, auch wenn es eh nichts sinnvolles enthält, und nur auf Assigned(Error) prüfen DError := AcquireExceptionObject as Exception; end; TThread.Synchronize(nil, procedure begin OnDownloadComplete(URL, Stream, DError); end); finally DError.Free; Stream.Free; HTTP.Free; end; end).Start; end; end. Wie kann ich hier abfragen, ob eine Exception vorliegt?
![]() if not Assigned(Error) then
|
![]() |
Ansicht |
![]() |
![]() |
![]() |
ForumregelnEs 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
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
![]() |
![]() |