Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Delphi Dateidownload (https://www.delphipraxis.net/192793-dateidownload.html)

DieFliege 19. Mai 2017 10:11

Dateidownload
 
Hallo zusammen,

wieder mal habe ich ein (vermutlich) kleines Problem.

Ich habe mir ein kleines Update-Programm geschrieben, was aus dem Netz eine Datei auf den PC kopiert. Das Funktioniert auch soweit prima, allerdings wollte ich den Bediener per verschiedene Panel davon unterrichten, was er gerade macht.
Beispiel:
Panel1: "Es steht eine neue Version zur Verfügung."
Panel2: "Der Download wurde gestartet, bitte warten!"
Panel3: "Der Download wurde abgeschlossen."

Diese Panel blende ich per 'visible' und 'true' oder 'false' entweder ein oder aus - zumindest wollte ich das.

Panel1 wird mit dem Start des Programms eingeblendet. Klicke ich auf "weiter" (btn_weiter), wird mir zwar das Panel1 ausgeblendet aber nicht das Panel2 eingeblendet. Dies geschieht erst nachdem die Datei runter geladen wurde. Geprüft habe ich es, indem ich "Panel3.Visible := True;" raus genommen habe.

Nun die Frage: Warum wird Panel2 erst nachdem ich den Download-Befehl gegeben habe eingeblendet? Das Panel1 wird korrekt ausgeblendet. Hier mein Quelltext:

Delphi-Quellcode:
procedure DeleteIECache;
var
  lpEntryInfo: PInternetCacheEntryInfo;
  hCacheDir: LongWord;
  dwEntrySize: LongWord;
begin
  dwEntrySize := 0;
  FindFirstUrlCacheEntry(nil, TInternetCacheEntryInfo(nil^), dwEntrySize);
  GetMem(lpEntryInfo, dwEntrySize);
  if dwEntrySize > 0 then lpEntryInfo^.dwStructSize := dwEntrySize;
  hCacheDir := FindFirstUrlCacheEntry(nil, lpEntryInfo^, dwEntrySize);
  if hCacheDir <> 0 then
  begin
    repeat
      DeleteUrlCacheEntry(lpEntryInfo^.lpszSourceUrlName);
      FreeMem(lpEntryInfo, dwEntrySize);
      dwEntrySize := 0;
      FindNextUrlCacheEntry(hCacheDir, TInternetCacheEntryInfo(nil^), dwEntrySize);
      GetMem(lpEntryInfo, dwEntrySize);
      if dwEntrySize > 0 then lpEntryInfo^.dwStructSize := dwEntrySize;
    until not FindNextUrlCacheEntry(hCacheDir, lpEntryInfo^, dwEntrySize);
  end;
  FreeMem(lpEntryInfo, dwEntrySize);
  FindCloseUrlCache(hCacheDir);
end;
Delphi-Quellcode:
procedure TForm1.btn_weiterClick(Sender: TObject);
var
  quelldatei : String;    //-- Quellpfad im Web
  zieldatei : String;    //-- Zielpfand auf Rechner

begin
  Panel2.Visible := True;
  Panel1.Visible := False;

  quelldatei := 'http://www.irgendeine-seite.de/dateiname.typ';
  zieldatei := 'c:\Programme\mein Programm\dateiname.typ';

  DeleteIECache;         //-- Cache leeren damit Datei auch tatsächlich aus dem Web geladen wird

  UrlDownloadToFile(nil, PChar(quelldatei), PChar(zieldatei), 0, nil);
  //Panel3.Visible     := True;
  Panel2.Visible     := False;
  btn_weiter.Visible := False;
  btn_fertig.Visible := True;
end;
Habt Ihr eine Idee? Vielen Dank schon einmal!

LG DieFliege

Aviator 19. Mai 2017 10:20

AW: Dateidownload
 
Du könntest zum Testen ein
Delphi-Quellcode:
Application.ProcessMessages;
hinter
Delphi-Quellcode:
Panel1.Visible := False;
einbauen.

Aber das ist wie gesagt nur ein Test. Schöner wäre es, wenn du die Methode
Delphi-Quellcode:
UrlDownloadToFile()
in einen eigenen Thread auslagerst. Ich vermute einfach mal, dass das noch nicht passiert ist. So wie es jetzt ist, wird deine GUI während des Downloads nicht korrekt neu gezeichnet da sie ja mit dem Herunterladen der Datei beschäftigt ist.

DieFliege 19. Mai 2017 10:27

AW: Dateidownload
 
Hallo Aviator,

vielen Dank für Deine Antwort.

Es hat tatsächlich schon mit "Application.ProcessMessages;" geholfen und Du hast natürlich recht, das Programm wartet solange bis der Download abgeschlossen ist. Natürlich bin ich daran interessiert, wie es funktioniert, wenn man den Download in einen Thread auslagert. Werde dazu mal auf die Suche gehen, wie man dies bewerkstelligt.

Vielen Dank erst mal!

LG DieFliege

sko1 21. Mai 2017 08:09

AW: Dateidownload
 
Und anstelle mehrere Panels ein und auszublenden solltest Du nur eines verwenden und dessen Beschriftung ändern!

Ciao
Stefan

SneakyBagels 21. Mai 2017 11:01

AW: Dateidownload
 
Hier hast du eine vernünftige Lösung

Stackoverflow: How to download a file with progress with IdHTTP via https

Das https kannst du ignorieren es geht hier nur ums Prinzip wie man mit IdHTTP und Work, Workbegin und WorkEnd arbeitet.

haentschman 21. Mai 2017 11:29

AW: Dateidownload
 
Hallöle...:P
Zitat:

Hier hast du eine vernünftige Lösung
...ich habe mich nicht getraut das anzusprechen. :roll:
Hinweis für TE: Das löst aber nicht das Auslagern in einen Thread Problem...

SneakyBagels 21. Mai 2017 11:50

AW: Dateidownload
 
Zitat:

Hinweis für TE: Das löst aber nicht das Auslagern in einen Thread Problem...
Ich benutze oben genannten Code selber in einem Thread. Keinerlei Probleme.

haentschman 21. Mai 2017 11:54

AW: Dateidownload
 
So meinte ich das nicht. :P Er hat ja noch keinen Thread. Deshalb muß er immer noch auf Application.ProcessMessages ausweichen. :zwinker: Soll heißen... auch mit den Indys ist der Download blockierend.

SneakyBagels 21. Mai 2017 11:57

AW: Dateidownload
 
Vielleicht so?

Benötigt: http://stackoverflow.com/questions/2...59305#28459305

Delphi-Quellcode:
// Variablen
updateThread: TUpdateThread
HTTPFileDownload: THTTPFileDownload;
procedure IdHTTPProgressOnChange(Sender: TObject);

// Initialisierung
updateThread := nil;

// Im Buttonclick
if updateThread = nil then
 updateThread := TUpdateThread.Create;

// Thread-Konstrukt
type
 TUpdateThread = class(TThread)
 protected
  procedure Execute; override;
 public
  constructor Create;
  destructor Destroy; override;
 end;

constructor TUpdateThread.Create;
begin
 inherited Create(False);
end;

destructor TUpdateThread.Destroy;
begin
 inherited Destroy;
end;

procedure TUpdateThread.Execute;
begin
 // Downloade Datei und und und...

 Form1.HTTPFileDownload := THTTPFileDownload.Create(nil);
 Form1.HTTPFileDownload.OnChange := Form1.IdHTTPProgressOnChange;

 try
  if Form1.HTTPFileDownload.DownloadFile(sURL, sDestinationOnDisk) then
   begin
    // Datei heruntergeladen
   end else
    begin
     // Fehler behandeln
    end;
 finally
  Form1.HTTPFileDownload.Free;
 end;

// Progress
procedure TForm1.IdHTTPProgressOnChange(Sender: TObject);
var
 sTmp: string;
 iDownloaded: Int64;
 iPercent: Integer;
begin
 iPercent := THTTPFileDownload(Sender).Progress;
 iDownloaded := HTTPFileDownload.BytesTransfered div 1024;

 if iPercent = 100 then
  Exit;

 if (iPercent mod 5 = 0) then
  begin
   Sleep(1);

   // sTmp := // string zusammenbasteln aus u.a.
   // iPercent, iDownloaded

   // Beispielausgabe mit entsprechenden Anpassungen im Execute-Teil, bei mehreren Dateien:
   // File 6/12: Downloading (91% | 964 KB/1061 KB)  
  end;
end;
Deine Prozedur DeleteIECache kannst du löschen. Es gibt schon etwas Fertiges. Einfach Winapi.WinInet.DeleteUrlCacheEntry(filename) aufrufen.

haentschman 21. Mai 2017 12:03

AW: Dateidownload
 
Zitat:

Vielleicht so?
...definitiv NÖ! :zwinker:
Delphi-Quellcode:
procedure TUpdateThread.Execute;
begin
 // Downloade Datei und und und...

 Form1.HTTPFileDownload := THTTPFileDownload.Create(nil);
 Form1.HTTPFileDownload.OnChange := Form1.IdHTTPProgressOnChange;
...
...ohne Synchronisierung auf die Form Variable im Thread zugreifen ist ein No-Go. Wieso soll der Thread die Form kennen? :gruebel:

SneakyBagels 21. Mai 2017 12:07

AW: Dateidownload
 
Ist jetzt einfach abgetippt. Verwende ich schon seit etlichen Jahren so und bisher hat sich niemand beschwert (von XP bis Windows 10).
Ich greife doch nicht auf irgendeine VCL-Komponente zu, so schlimm kann es also nicht sein.

haentschman 21. Mai 2017 12:14

AW: Dateidownload
 
Zitat:

Verwende ich schon seit etlichen Jahren so und bisher hat sich niemand beschwert (von XP bis Windows 10).
...ich empfinde das als Zufall. :?
Zitat:

Ich greife doch nicht auf irgendeine VCL-Komponente zu, so schlimm kann es also nicht sein.
...was ist Form1? :gruebel:

SneakyBagels 21. Mai 2017 12:26

AW: Dateidownload
 
Zitat:

...ich empfinde das als Zufall.
Denke ich nicht. So schlimm kann es nicht sein, wie hier immer gepredigt wird. Denn wenn es tatsächlich so wäre, dann hätten andere und ich in den letzten, etlichen Jahren viele viele Fehler sehen müssen.

Frage:
wäre es denn besser ein Record in einer Unit ohne Form abzulegen, eine Instanz zu erzeugen und auf diese Daten zuzugreifen? Dann wäre Form1 weg.
Dann wäre es wenigstens ein wenig OOP und Synchronize bräuchte man auch nicht mehr.

haentschman 21. Mai 2017 13:44

AW: Dateidownload
 
Zitat:

So schlimm kann es nicht sein, wie hier immer gepredigt wird. Denn wenn es tatsächlich so wäre...
An dieser Stelle bin ich raus. :? Ich habe mir kürzlich mein Maul verbrannt als ich das die Nachteile des WITH erklären wollte. Das brauche ich nicht nochmal. :? Ich hoffe daß du auch von anderer Stelle die Fehlerquellen dieser Konstellation gezeigt bekommst. :?

Wenn du es erklärst haben möchtest, mache bitte einen anderen Thread auf.

SneakyBagels 21. Mai 2017 13:52

AW: Dateidownload
 
Zitat:

An dieser Stelle bin ich raus.
Das beantwortet meine Frage aber nicht, die den TE auch interessieren könnte.

haentschman 21. Mai 2017 14:19

AW: Dateidownload
 
Tutorial Threads: https://wiki.delphigl.com/index.php/...Multithreading

Zitat:

Zitat:

Alles was aus dem Execute aufgerufen wird hat als Kontext diesen Thread!
Zitat:

Jetzt stellt sich natürlich die Frage was muss alles synchronisiert werden?
Pauschal lässt sich nur sagen, alles was irgendwo von mehreren Threads (incl. VCL-Thread) geschrieben werden kann muss synchronisiert werden. Ich erkläre euch das mal besser an einem kleinem Beispiel:

Nehmen wir einmal an wir haben einen Webspider. Dieser soll 200 Webseiten downloaden.
Wir erstellen uns einen Thread dem ich die URL übergebe und der selbständig mit dem Laden anfängt. Wenn dieser fertig ist dann sagt er dem VCL-Thread mittels eines Events, dass er fertig ist.
Grundsätzlich werden die "Werte der Berechnung z.B" nur über Events rausgegeben. Diese werden IMMER synchronisiert.
Delphi-Quellcode:
type
  TBinFertig = procedure(const Content: String) of object;

  TMyOwnThread = class(TThread)
  private
    FBinFertig: TBinFertig;
    procedure SyncBinFertig;
  public
    property BinFertig: TBinFertig read FBinFertig write FBinFertig;
  end;

implementation

procedure TMyOwnThread.SyncBinFertig;
begin
  if Assigned(FBinFertig)
    then FBinFertig(DasIstDerInhaltDerWebseite);
end;

procedure TMyOwnThread.Execute;
begin
  try
    // Download der Seite ...

    // Synchronisieren
    Synchronize(SyncBinFertig);
  except
    on e: exception do begin
      // mache hier irgendetwas mit dem Fehler.
    end;
  end;
end;

SneakyBagels 21. Mai 2017 14:24

AW: Dateidownload
 
Heißt das also, dass man auch simple Variablen eines Records (NICHT Form1.xyz) synchronisieren muss?

Zum Beispiel EinRecord.sHTML := {SeiteDownloaden}; ?

haentschman 21. Mai 2017 15:09

AW: Dateidownload
 
Solange der Record NUR im Thread Kontext Verwendung findet ist es entspannt. Ansonsten SYNCRONIZE und Event...

HolgerX 21. Mai 2017 15:16

AW: Dateidownload
 
Hmm..

(Wiederholt in mehreren Posting hier im Forum!!!) ;)

Zitat:

Zitat von SneakyBagels (Beitrag 1372233)
Heißt das also, dass man auch simple Variablen eines Records (NICHT Form1.xyz) synchronisieren muss?

Zum Beispiel EinRecord.sHTML := {SeiteDownloaden}; ?



Wenn auf Variablen sowohl innerhalb des Threads(Execute) wie auch von außerhalb des Threads (Form.OnButtonClick) zugegriffen werden,
dann MUSS IMMER Threadsave (Sychronisiert/Critical Section/SendMessage) gearbeitet werden!!!

SneakyBagels 21. Mai 2017 15:23

AW: Dateidownload
 
Man muss nur immer sehen, ob Synchronize oder CriticalSection für einen angemessen ist denn beide tun unterschiedliche Dinge soweit ich weiß.

Laut diesem Thread kann man auch mehreren Threads dieselbe CiriticalSection verpassen http://www.delphipraxis.net/29491-sy...der-sonst.html
Nach dem Motto... CriticalSection.Enter; erzeuge 2 threads und lass sie arbeiten, CriticalSection.Leave;

Könnte man so also den Downloadthread starten und ohne Synchronize im Thread selber alles anfassen und ändern? Denn drumrum liegt ja die CriticalSection oder nicht?

Anderenfalls wenn es nur eine Instanz gibt ist das hier vielleicht die Lösung
http://www.delphipraxis.net/1137936-post3.html Post #3


Alle Zeitangaben in WEZ +1. Es ist jetzt 20:07 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-2025 by Thomas Breitkreuz