Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Update-Vorgang in einen Thread auslagern (https://www.delphipraxis.net/183191-update-vorgang-einen-thread-auslagern.html)

mm1256 18. Dez 2014 12:04

Update-Vorgang in einen Thread auslagern
 
Hallo Delphianer,

vorsicht, es wird ein kleiner Roman, aber, ich versuche so weit es geht Missverständnisse und sicher auch gut gemeinte Vorschläge über Alternativen von vorneherein auszuschließen. Und dazu braucht es eben auch ein paar Zeilen.

Ich habe für meine SW ein Update-Programm, das über ein mit Blowfish gesichertes TCP/IP-Protokoll eine Datenbank-Verbindung zu meinem Update-Server (NexusDB-Server) herstellt, eventuell neuere Dateien herunterlädt, und dann installiert. Der selbe DB-Server (nur eine andere Datenbank) wird für die Bereitstellung der Lizenzen verwendet. Soweit nichts besonderes. Läuft auch seit 2 Jahren prima.

Wenn ich meine SW mit meinem selbst geschriebenen Installationsprogramm installiere, ist der Ablauf (Ausschnitt) wie folgt:

1. Der User gibt die Lizenznummer ein
2. Die Lizenz(datei) wird heruntergeladen
3. Installation wird fortgesetzt

Nach der Installaton wird meine SW mit Admin-Rechten gestartet und die SW ruft dann in festgelegten Zeiträumen das Updateprogramm auf. Das passiert normalerweise nach einer Update-Installation via CD beim ersten Programmstart.

Und nun das eigentliche Problem: Die Firewall blockiert das Updateprogramm natürlich bei der Erstinstallation und nach jeder Aktualisierung. Wir wissen ja, was User alles weg klicken, oder wohin sie "nicht" klicken, wenn sie sollen. Stichwort "Ach ja, die Meldung kommt immer..:"

Der Gedanke ist also, wenn ich bei der Installation die Lizenz abfrage (da passen die User noch richtig auf, weil sie die Lizenz ja installieren wollen/müssen) und anschließend herunterlade, dann habe ich ja zu dem Zeitpunkt schon eine aktive Verbindung und könnte doch in der Zeit wo ich die SW installiere, parallel dazu eventuell vorhandene Updates herunterladen.

- Das sollte ein eigener Thread sein, damit die Installation ungehindert fortgesetzt wird
- Das Installationsprogramm muss den Fortschritt des Downloadvorganges abfragen können.
- Wenn die Installation fertig und der Download noch nicht abgeschlossen ist, sollte auch der Downloadfortschritt angezeigt werden können
- Der Code sollte so "verpackt" sein, dass er auch vom eigentlichen Updateprogramm verwendet werden kann. Im Updateprogramm sind getrennt: Form (das was der User sieht), Logik (der Downloadvorgang, das Ersetzen der vorhandenen Dateien) in einer Unit, und der Datenbankzugriff in einem Datenmodul.

Nun habe ich über Threads schon einiges gelesen, so richtig kapiert hab ich das allerdings noch nicht. Noch dazu gibt's ja - wenn man sich die Beiträge hier so ansieht - auch sehr unterschiedliche und differenzierte Meinungen und Ansichten, wie man Threads einsetzt bzw. damit umgeht. Das macht es für mich nicht einfacher.

Ich bin Autodidakt, kein studierter Informatiker. Damit ich aber bei Problemen reagieren kann, muss ich das Ganze auch "verstehen" können. Darum meine Frage und Bitte nach Vorschlägen und Anregungen. Gerne gegen Bezahlung....in dem Fall bitte PN.

Mavarik 18. Dez 2014 12:33

AW: Update-Vorgang in einen Thread auslagern
 
Nicht das ich hier jemandem den Job wegnehmen will, aber Bezahlung wird nicht nötig sein.

Ob Du einen Download aufrufst und warten musst oder es in einen Thread auslagerst ist "erst mal" das Gleiche.

Mit zwei unterschieden:
1. Deine Application läuft mit anderen aufgaben weiter.
2. Du musst in Deiner Download-Procedure Zugriffe auf ein Form oder die VCL syncronisieren. (Bedeutet mit Syncronize eine Procedure aufrufen, die z.B. eine Processbar updatet)

Die eigentliche Frage lauten: An welcher Stelle kommst Du nicht weiter?

Mavarik

mm1256 18. Dez 2014 13:42

AW: Update-Vorgang in einen Thread auslagern
 
Hallo,

genauso einfach hab ich mir das ja vorgestellt...

Delphi-Quellcode:
unit uUpdateThread;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type

  TMyUpdateThread = class(TThread)
  protected
    procedure Execute; override;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
    FMyUpdateThread: TMyUpdateThread;
    function UpdateStart: boolean;
  end;

var
  Form1: TForm1;

implementation

uses Dat_Glo, // Globales Datenmodul
     Update_Server; // Update-Form

{$R *.dfm}

procedure Execute_INTERACTIVE; // Schnittstelle zur Update-Form in Update_Server.pas
begin
  Application.CreateForm(TFrmUpdate_Server, FrmUpdate_Server); // Update-Form
  FrmUpdate_Server.Execute; // Update durchführen
end;


procedure TMyUpdateThread.Execute;
begin
  Execute_INTERACTIVE;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  UpdateStart;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FMyUpdateThread := nil;
end;

function TForm1.UpdateStart: boolean;
begin
  Result := true;
  FMyUpdateThread := TMyUpdateThread.Create(True);
  try
    FMyUpdateThread.FreeOnTerminate := True; // Gibt sich selber frei
    FMyUpdateThread.Resume; // Start
  except
    on E:Exception do begin
      Result := false;
      ShowMessage(E.Message);
    end;
  end;
end;

end.
Und jetzt scheitert es an der Synchronisierung. Also wie bekomme ich mit, wie weit der Download gerade ist (der Stand des Progressbar in FrmUpdate_Server)

Außerdem ist mir nicht klar, wann der Thread "vollkommen" fertig ist, damit ich FrmUpdate_Server wieder freigeben kann.

Mavarik 18. Dez 2014 13:47

AW: Update-Vorgang in einen Thread auslagern
 
OK! Erst mal in einem Thread kein FormCreate!

Der eigentlich download muss in den Thread...
Es gibt ein nettes Beispiel für Anonyme Threads.
Das Beispiel zeigt Dir auch direkt den Sync-Aufruf für Process & Ende...

Mavarik

mm1256 18. Dez 2014 14:29

AW: Update-Vorgang in einen Thread auslagern
 
Hallo,

erst mal vielen Dank für deine Bemühungen. Anonymer Thread bedeutet doch, dass die darin erstellten Objekte nicht geshared werden? Hmmmm....bedeutet das, dass ich dann außerhalb des Threads auf die Eigenschaften der Form zugreifen kann, also beispielsweise den Status des Progressbar abfragen? Weil ich die Form ja auch außerhalb des Thread erstellt habe. Das wäre ja einfach, dann müsste ich nur noch ein paar properties einbauen. Aber dann muss doch der Thread irgendwie die Form aktualisieren...oder stehe ich jetzt total auf dem Schlauch.

Hast du mal bitte einen Link auf das obengenannte Beispiel? Hier habe ich unter "Anonyme Threads" nichts prickelndes gefunden.

Sir Rufo 18. Dez 2014 15:59

AW: Update-Vorgang in einen Thread auslagern
 
[OT]
Auch wenn du es nicht hören willst, ich sage es trotzdem (nicht das nachher noch jemand sagt, warum das keiner gesagt hat, und ich mir dann denke, warum hast du das denn nicht einfach gesagt ...):

Wenn du den Krams umstellst auf
  • Kommunikation per HTTP(S) (löst die Firewall-Problematik)
  • Setup-Programm mit InnoSetup (oder wer auch immer) (löst die Nachlade-Problematik)
dann hast du diese ganzen Probleme nicht mehr und du beschreitest alte, bewährte und funktionierende Trampelpfade.

Aber du möchtest es ja nicht hören ... :stupid:
[/OT]

QuickAndDirty 18. Dez 2014 16:45

AW: Update-Vorgang in einen Thread auslagern
 
Ich habe Threads zuletzt immer über
TMultiReadExclusiveWriteSynchronizer oder TCriticalSection synchronisiert.

Um Nachrichten zwischen zwei Threads auszutauschen kann mann sich z.B.
ein Daten-Objekt bauen und dies in einer CriticalSection übergeben.
Sowohl Lesen als auch schreiben muss dann in einer CriticalSection in beiden Threads geschehen.
Oft macht es Sinn nicht einfach nur einzelnen Eigenschaften des Daten-Objects zu lesen sondern,
statt dessen mit einer Kopie zu arbeiten.
Denn die Kopie muss nicht in einer CriticalSection gefasst werden.

Wenn man nicht mit Kopien arbeiten will nur um das Datenobjekt von verschiedenen Threads auslesen zu lassen kann man
TMultiReadExclusiveWriteSynchronizer
Verwenden.
Der Blockt das lesen nur dann wenn gerade geschrieben wird. BZW.blockt das schreiben bis das Lesen beendet wurde.
Dieses Verfahren ist in Versorger/Verbraucher Konstellationen brauchbar.


Wenn Threads hauptsächlich "Warte"
Kommt man mit TEvent und und WaitForSingleObject oder WaitForMultipleObjects weiter
je nach dem ob man auf ein Event oder auf mehrere wartet.

mm1256 18. Dez 2014 16:56

AW: Update-Vorgang in einen Thread auslagern
 
Zitat:

Zitat von Sir Rufo (Beitrag 1283986)
Aber du möchtest es ja nicht hören ...

Nein, der Eindruck täuscht, das stimmt so wirklich nicht. Wenn ich so beratungsresistent wäre, dann würde ich (respektive meine SW bzw. Firma) anders da stehen. Die Frage ist doch anders herum die, warum ich meine eigene Installationsroutine verwende, und nicht Inno oder Installshield oder....sonst was. Das hatte ich schon. Bin damit auch glücklich gewesen, bis ich an den Punkt kam, wo es nicht mehr weiter ging. Wo diese Installationsprogramme einfach versagten, wo ich zwar Erweiterungen Plugins usw. in die Installation einbauen kann, um spezielle Aufgaben zu erledigen, aber dann kann ich den Rest auch gleich erledigen. Da geht es um mehr als nur eine SW zu installieren und sie über's WEB upzudaten.

Ich möchte dich/euch damit nicht langweilen, aber was da (an dem Installationsprogramm) alles dran hängt, das ist mit keinem Standard-Tool wie Inno & Co lösbar. Wenn es dich/euch interessiert, dann bin ich sehr gerne bereit mal einen Einblick geben, was da alles drin und dran steckt. Dann machen wir woanders (beispielsweise unter "Sonstige Werkzeuge") einen neuen Thread dafür auf.

Weil das aber nicht Bestandteil meines Problems ist, wollte ich "alternative Vorschläge" vermeiden. Diese führen in der Regel zu Diskussionen die in der Sache selber nicht weiterbringen. Dafür gibt es hier und in nahezu jedem größeren Internetforum endlose Beispiele.

Sir Rufo 18. Dez 2014 17:06

AW: Update-Vorgang in einen Thread auslagern
 
Möglicherweise bist du nicht beratungsresistent, aber dir fehlt es gerade an Fantasie ;)

Mit InnoSetup kann ich mir wunderbar etwas zusammenbauen, was mir aus dem Internet etwas lädt und installiert/ausführt. Ist das auf bestimmte Sachen beschränkt? Nö, muss ja nur etwas ausführbares sein. Also ich könnte mein eigenes Installationsprogramm laden/nachladen wenn nicht aktuell und ausführen. Ja, das ist möglich.

Did you get the point?

mm1256 18. Dez 2014 17:14

AW: Update-Vorgang in einen Thread auslagern
 
Hallo Andreas,

was du ansprichst sind genau die Dinge, von denen ich zu wenig bis gar keine Ahnung bzw. Praxis habe. Das Installationsprogramm benutzt Units die globale Ressourcen bereitstellen, z.B. die Registry-Einträge die in einem Rutsch beim Programmstart eingelesen werden, und wenn das Programm geschlossen wird, wieder in einem Rutsch zurück geschrieben werden. Das minimiert Registry-Zugriffe. Das Updateprogramm greift auch auf diese globalen Strukturen zu. Also müsste man da "vermutlich" mit Critical Sections arbeiten. Da das aber insgesamt über einen Zeitraum von mehreren Minuten gehen kann, und Critical Sections eher kurz gehalten werden sollen, wird es wieder (zumindest für mich) komplizierter bzw. komplexer, als es auf Anhieb aussieht.

Darum war der ursprüngliche Ansatz (siehe Code) der, dass ich die ganze Form und deren Routinen in den Thread hinein packe. Was ich aber lt. Mavarik nicht tun sollte.

Mich beschleicht langsam der Gedanke, dass ich mit einer separaten DLL, deren update-ausführende Routine ich in einem Thread lade, dem Ziel näher komme. Dann habe ich zwar keine Synchronisation, aber weniger (Speicher-)Probleme, und ich weiß am Ende der Installation auch, ob der Download läuft oder nicht. Wäre das nicht eine Alternative?

@Sir Rufo
Zitat:

Möglicherweise bist du nicht beratungsresistent, aber dir fehlt es gerade an Fantasie
Na gut, dann habe ich also keine Fantasie. Damit kann ich leben, und du darfst weiterhin mit Inno glücklich sein. Was wollen wir denn mehr?

Dejan Vu 19. Dez 2014 07:32

AW: Update-Vorgang in einen Thread auslagern
 
Die Kommunikation zwischen Threads und der Außenwelt geschieht über synchronisierte Events und geschützte Properties.

Der Thread ackert also im Hintergrund, möchte aber, das sein innerer Zustand (Was macht er gerade? Wie weit ist er?) in einer Form sichtbar ist.

Fein. Dann unterhalten die sich eben, aber nicht direkt, bitte. Das ist praktisch, weil so eine Form auch gut für andere Threads verwendet werden kann und andererseits so ein Thread auch mit einer anderen Form reden kann (oder mit gar keiner, sollte ihm schnurz sein).

Es bieten sich zum Unterhalten Events oder Interfaces an. Beides muss aber synchronisiert werden, d.h. Thread und Außenwelt laufen ja in unterschiedlichen Threads und die VCL ist nicht threadsafe, d.h. alles, was mit der VCL geschieht, muss im Hauptthread gemacht werden.

Nehmen wir mal folgenden Thread;
Delphi-Quellcode:
Procedure TMyThread.Execute;
Begin
  ShowProgressBar();
  MaxSteps := 1000;
  For i:=1 to MaxSteps do begin
     Self.Progress := I;
     UpdateProgressBar();
     DoSomething();
  end;
  HideProgressBar();
End;
Die fraglichen Methoden sind also 'ShowProgressBar', 'UpdateProgressBar' und 'HideProgressBar'.
In jedem Fall müssen die erst einmal per 'Synchronize' (oder 'Queue') aufgerufen werden, denn diese beiden Methoden sorgen dafür, das die Aufrufe im Kontext des VCL-Threads durchgeführt werden.
Also:
Delphi-Quellcode:
Procedure TMyThread.Execute;
Begin
  Synchronize(ShowProgressBar);
  MaxSteps := 1000;
  For i:=1 to MaxSteps do begin
     Self.Progress := I;
     Synchronize(UpdateProgressBar);
     DoSomething();
  end;
  Synchronize(HideProgressBar);
End;
Ok. Nun gibt es (mindestens) zwei Ansätze, wie die konkrete Kommunikation mit einem Formular aussehen kann.
1. Mit Events. Der Thread deklariert drei Eventhandler ,'OnShowProgressBar', 'OnHideProgressBar' und 'OnUpdateProgressBar' (geht auch mit einem Event, aber egal). Das Formular meldet sich auf die Events an und führt die VCL-Änderungen (Progressbar zeigen, verbergen, updaten) aus. So ungefähr
Delphi-Quellcode:
Type
  TMyThread = Class...
  public
     OnShowProgressBar : TNotifyEvent Read FOnShowProgressBar Write FOnShowProgressBar;
     OnHideProgressBar : TNotifyEvent Read FOnHideProgressBar Write FOnHideProgressBar ;
     ...
   end;

Procedure TMyThread.ShowProgressbar;
Begin
  if Assigned (FOnShowProgressBar) Then
     FOnShowProgressBar(Self);
End;
...

Procedure TMyForm.StartThread;
Begin
  FmyThread := TmyThread.Create;
  FmyThread.OnShowProgressBar := ShowProgressBar;
  FmyThread.OnHideProgressBar := HideProgressBar;
...
End;

Procedure TMyForm.ShowProgressBar (Sender : TObject);
Begin
  Progressbar.Visible := True;
End;
Oder mit einem Interface. Dabei erwartet der Thread eine 'View', die die drei Methoden zum Anzeigen der Progressbar bereitstellt.
Delphi-Quellcode:
Type
  IProgessBar = interface
    procedure ShowProgressBar;
    Procedure HideProgressBar;
    Procedure UpdateProgressBar (Position, Total : Integer);
  End;

  TMyThread = class (TThread)
    FProgess : IProgressBar;
  public
    Constructor Create (aProgress : IProgress);
  End;

...
Procedure TMyThread.ShowProgressBar;
Begin
  if Assigned (FProgress) then FProgess.ShowProgressBar;
End;

Procedure TMyThread.UpdateProgressBar;
Begin
  if Assigned (FProgress) then FProgess.UpdateProgressBar (Self.Position, Self.Total);
End;
....

Type
  TMyForm = Class (TForm, IProgress)
  ...
    Procedure ShowProgressBar;
    Procedure HideProgressBar;
    Procedure UpdateProgressBar (Position, Total : Integer);
End;
Das geht alles viel einfacher und besser, aber das Prinzip sollte klar sein. Standardpattern bei Delphi sind die Events. In anderen Programmiersprachen eher die Interfaces (Java kennt z.B. gar keine Delegaten).

Blup 19. Dez 2014 09:30

AW: Update-Vorgang in einen Thread auslagern
 
Eigentlich ist das ein Fall für den asynchronen Prozeduraufruf.
Ältere Delphi-Versionen unterstützen das noch nicht.

In jedem Fall muss der Hauptthread die Ereignisse selbst auslösen.

A) Der Thread sendet Informationen über den Fortschritt mit Postmessage als Botschaft an ein Fenster, eine Komponente verarbeitet die Nachrichten und löst die Ereignisse aus.
Es ist aber nicht zu 100% sichergestellt, das wirklich alle Nachrichten auch den Empfänger erreichen, insbesondere wenn sehr viele Nachrichten in kurzer Zeit anfallen.

B) Der Thread legt Informationen über den aktuellen Progressstatus in einem geschützten Record ab, der Hauptthread prüft diesen Record periodisch (OnIDLE der Anwendung oder Timer).
Dabei können aber auch einzelne Schritte übersprungen werden, insbesondere wenn der Thread sehr schnell arbeitet.

C) Der Thread legt jede Ereignis z.B. als Objekt mit allen Informationen in einer geschützten Liste ab, der Hauptthread prüft die Liste periodisch (OnIDLE der Anwendung oder Timer).
Der Aufwand ist wahrscheinlich größer, das scheint aber die sicherste Variante zu sein.

Dejan Vu 19. Dez 2014 09:34

AW: Update-Vorgang in einen Thread auslagern
 
Na, asynchron muss das Update der Progressbar hier nun doch nicht sein. Besser wäre es natürlich, aber es geht auch so.

Bei zeitkritischen Threads, die ihre Zeit nicht mit dem Update einer UI verplempern wollen, ist das natürlich anzuraten (wobei dann ein Timer in der UI noch einfacher umzusetzen ist. EDIT: Ach, das ist ja deine Antwort 'B').

TakkaTukka 20. Dez 2014 10:44

AW: Update-Vorgang in einen Thread auslagern
 
Hallo !

Zitat:

1. Mit Events. Der Thread deklariert drei Eventhandler ,'OnShowProgressBar', 'OnHideProgressBar' und 'OnUpdateProgressBar' (geht auch mit einem Event, aber egal). Das Formular meldet sich auf die Events an und führt die VCL-Änderungen (Progressbar zeigen, verbergen, updaten) aus.
Hierzu habe ich eine Frage:
Wie muss das in der Form1 dann aussehen (komplette Deklaration), ich meine damit durch den Thread ein Event in Form1 oder Form2, .... ausgelöst wird, das ist mir vollkommen unklar ?
Ich denke damit kann man dann an mehreren Stellen im Programm auf Änderungen reagieren oder (ich denke hier z.B. an die Umstellung des Währungsformats, der Nachkommastellen, ....) ?

Mavarik 20. Dez 2014 11:05

AW: Update-Vorgang in einen Thread auslagern
 
Das mit dem Interface sieht natürlich klasse im Source aus...

Aber warum sendest Du nicht einfach aus dem Thread eine User-Message über Windows und schon sparst Du Dir das Syncronize!

Delphi-Quellcode:
Const
         WM_PShow    = WM_User + 400;
         WM_PHide    = WM_USer + 401;
         WM_PUpdate  = WM_USer + 402;

type
         TMyForm = class(TForm)
           private
             procedure WMShowPBar(var Msg:Tmessage); message WM_PShow;
             procedure WMHidePBar(var Msg:Tmessage); message WM_PHide;
             procedure WMUpdatePBar(var Msg:Tmessage); message WM_PUpdate;
         end;
Für Update einfach im WParam 0..100% übergeben und fertig.

Mavarik

Dejan Vu 20. Dez 2014 11:21

AW: Update-Vorgang in einen Thread auslagern
 
Hey Mavarik: Messages hab ich total vergessen. Sehr schöne Möglichkeit, speziell mit 'PostMessage' (asynchron)

Was mir daran als Pattern nicht so sehr gefällt ist die etwas problematische Übergabe komplexerer Informationen. Denn die müssen im Thread instantiiert und im Messagehandler (beim Postmessage) wieder freigegeben werden. Geht, ist sauber, aber imho nicht so schön (reine Geschmackssache)

Was mir daran allerdings gefällt, ist der äußerst niedrige Footprint zum Erzeugen von asynchronen Updatemöglichkeiten.

Zitat:

Zitat von TakkaTukka (Beitrag 1284165)
Wie muss das in der Form1 dann aussehen (komplette Deklaration)

Delphi-Quellcode:
Type
  TForm1 = class (TForm)
  ...
    Procedure UpdateProgressBar (Sender : TObject); // Das ist die Signatur eines TNotifyEvent
  ...

Procedure TForm1.Button1Click(Sender : TObject);
Begin
  FMyThread := TMyThread.Create;
  FMyThread.OnUpdateProgressBar := UpdateProgressbar; // Zuweisung des Eventhandlers an das Event
...
Zitat:

...(ich denke hier z.B. an die Umstellung des Währungsformats, der Nachkommastellen, ....
Dafür gibt es fertige Messages, die von Windows versandt werden. (siehe Mavariks Ansatz). Googel mal danach.

Sir Rufo 20. Dez 2014 11:32

AW: Update-Vorgang in einen Thread auslagern
 
Eventuell weil das zu stark mit der Plattform verbunden ist?

Weil man sich dafür einen MessageManager schreibt, den man dann für alles benutzen kann und vor allem verständliche und einfach zu handhabende Messages durch das System leiten kann.

Auf den Trichter ist auch Emba gekommen System.Messaging.

Den Nachrichtentypen definieren:
Delphi-Quellcode:
unit MyCustomMessages;

uses
  System.Messaging;

type
  TProgressMessage = class( TMessage )
  public
    constructor Create( Position, Max : Integer );
    property Position : Integer read FProgress;
    property Max : Integer read FMax;
  end;
Die Form damit verbinden
Delphi-Quellcode:
uses
  System.Messaging;

TFoo = class( TForm )
  Progressbar1 : TProgressBar;
private
  procedure HandleProgressMessage( const Sender : TObject; const M : TMessage );
public
  procedure AfterConstruction; override;
  procedure BeforeDestruction; override;
end;

implementation

uses
  MyCustomMessages;

procedure TFoo.AfterConstruction;
begin
  inherited;
  TMessageManager.DefaultManager.SubscribeToMessage( TProgressMessage, HandleProgressMessage );
end;

procedure TFoo.BeforeDestruction;
begin
  TMessageManager.DefaultManager.Unsubscribe( TProgressMessage, HandleProgressMessge );
  inherited;
end;

procedure TFoo.HandleProgressMessage( const Sender : TObject; const M : TMessage );
var
  LMsg : TProgressMessage absolute M;
begin
  ProgressBar1.Position := LMsg.Position;
  ProgressBar1.Max := LMsg.Max;
end;
Der Thread schmeisst die Nachricht
Delphi-Quellcode:
uses
  System.Messaging, MyCustomMessages;

procedure TFooThread.Execute;
var
  LPosition, LMax : Integer;
begin
 
  while WorkInProgress do
  begin
    // Fortschritts-Nachricht verschicken
    Queue(
      procedure
      begin
        TMessageManager.DefaultManager.SendMessage( Self, TProgressMessage.Create( LPosition, LMax ) );
      end );
    ...
  end;
end;
Und schon klappt das auf jeder Plattform, auch ohne irgendwelche Fenster (Handle) offen zu haben, egal wer das empfangen möchte, kann sich einfach an den MessageManager hängen und alles ist gut.

Mavarik 20. Dez 2014 11:34

AW: Update-Vorgang in einen Thread auslagern
 
Zitat:

Zitat von Dejan Vu (Beitrag 1284169)
Was mir daran als Pattern nicht so sehr gefällt ist die etwas problematische Übergabe komplexerer Informationen. Denn die müssen im Thread instantiiert und im Messagehandler (beim Postmessage) wieder freigegeben werden. Geht, ist sauber, aber imho nicht so schön (reine Geschmackssache)

Ja, das kommt darauf an was Du komplex nennest... Hierbei ging es "nur" um eine Zahl 0..100% (Byte)
Selbst wenn es ein mehr ist.. im thread ein guter alter Getmem und im Form der Freemem.
Da man das kaum noch verwendet - ist sofort klar - Oh ein Getmem... Da muss sich jemand anderes darum kümmern das wieder frei zu geben...

Mavarik

Mavarik 20. Dez 2014 11:42

AW: Update-Vorgang in einen Thread auslagern
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von Sir Rufo (Beitrag 1284172)
Und schon klappt das auf jeder Plattform, auch ohne irgendwelche Fenster (Handle) offen zu haben, egal wer das empfangen möchte, kann sich einfach an den MessageManager hängen und alles ist gut.

Klar, auch das sieht schick im Sourecode aus... Und ja... Natürlich hat Du wieder einmal Recht...:thumb:
Aber er sprach von einem eigenen Installer - Sieht mir nicht nach iOS/Android oder Mac aus...:stupid:

Also ist es - erst mal - nicht so schlimm, wenn es nur für Windows ist. Abgesehen davon spart man sich das Queue...:twisted:

Mavarik

PS.: Du schreibst jetzt 100x "Ich soll keinen kompletten Sourcecode im Forum posten"

Dejan Vu 20. Dez 2014 12:16

AW: Update-Vorgang in einen Thread auslagern
 
Zitat:

Zitat von Mavarik (Beitrag 1284173)
Ja, das kommt darauf an was Du komplex nennest... Hierbei ging es "nur" um eine Zahl 0..100% (Byte)
Selbst wenn es ein mehr ist.. im thread ein guter alter Getmem und im Form der Freemem.
Da man das kaum noch verwendet - ist sofort klar - Oh ein Getmem... Da muss sich jemand anderes darum kümmern das wieder frei zu geben...

Richtig. Ich meinte auch 'Pattern', also das, was ich versuche, grundsätzlich in Programmen zu verwenden.
Aber ansonsten hast Du natürlich vollkommen recht. Dein Ansatz ist absolut gleichrangig und reine Geschmackssache (oder Starrsinn), das ich es nicht verwende.

Also: bisher 4 schöne Pattern, um Aktualisierungen aus einem Thread in einen anderen zu übermitteln:
1. Events
2. Interfaces
3. Messages
4. Observer-Pattern (@Sir Rufo, einverstanden?)

Sir Rufo 20. Dez 2014 12:23

AW: Update-Vorgang in einen Thread auslagern
 
Zitat:

Zitat von Dejan Vu (Beitrag 1284178)
4. Observer-Pattern (@Sir Rufo, einverstanden?)

Heute ist der letzte Samstag der Woche? Ja, dann bin ich mal damit einverstanden :mrgreen:

QuickAndDirty 20. Dez 2014 12:34

AW: Update-Vorgang in einen Thread auslagern
 
Zitat:

Zitat von mm1256 (Beitrag 1284006)
Mich beschleicht langsam der Gedanke, dass ich mit einer separaten DLL, deren update-ausführende Routine ich in einem Thread lade, dem Ziel näher komme. Dann habe ich zwar keine Synchronisation, aber weniger (Speicher-)Probleme, und ich weiß am Ende der Installation auch, ob der Download läuft oder nicht. Wäre das nicht eine Alternative?

Das Problem ist ja, wenn es dann ein sehr langer download ist, weil z.B. gerade viel Bandbreite beim kunden für anderen Kram draufgeht. Wie oft haben selbst wir gedacht ein programm sei eingefroren, blos weil es keine Fortschrittsanzeige hat?

Es mag dich in keiner weise beeinflussen, aber ich finde es nicht notwendig parrallel updates runterzuladen. Ich mag alles schön der reihe nach. Es ist nur ein Setu. So oft macht man das nicht.

Wofür brachst du zum Starten eines downloads die Aufmerksamkeit des Benutzers?
Damit er sich auf einem Proxyanmeldet fals es sowas beim Kunden gibt?
Ich habe as Problem noch nicht vollständig erkannt dass du gedenkst mit einem parallel laufenden Download lösen zu wollen.
Man kann solche Downloads auf jeden Fall auch einfach in einem eigenen Prozess machen. Dann erübrigt sich die Thread-Problematik.

Dejan Vu 20. Dez 2014 14:35

AW: Update-Vorgang in einen Thread auslagern
 
Mir ist es ein Rätsel, wieso man den Download nicht einfach in einen kleinen Thread packt und mit einer der vier vorgestellten Methoden im Formular rummalt. Das kann doch nun jeder. Oder hab ich etwas verpasst?

mm1256 20. Dez 2014 19:22

AW: Update-Vorgang in einen Thread auslagern
 
Hallo zusammen,

so, ich war den halben Tag beschäftigt meinen Code umzubauen und die andere Hälfte mit Einkaufen (mit Frau...:thumb: ) und jetzt komme ich auch mal dazu, hier nachzuschauen was es neues gibt. Und war erst mal positiv überrascht von den vielen Antworten.

Also, Stand der Dinge: Zuerst war es von der Aufgabenverteilung so: Update-Form für das UI, eine Unit mit der Logik (Connect, Download, Disconnect, Installation der neuen programmdateien usw.) und das Datenmodul.

Das Datenmodul blieb bis auf ein paar kleine Änderungen wie es ist. Die Unit mit der Logik entfällt, die habe ich heute neu programmiert. Sie beinhaltet jetzt meinen Thread, der den Download durchführt.

Die Beispiele im WEB (nicht die von euch hier...die habe ich ja jetzt erst gelesen) hatten hinsichtlich der Synchronisierung eines gemeinsam: Der Thread muss mit dem aufrufenden Objekt kommunizieren, damit er z.B. die Fortschrittanzeige aktualisieren kann. So eine Vorgehensweise geht mir gegen den Strich. Der Thread soll sich um den Download kümmern müssen, und sonst nichts! Das war der Ansatz. Und, aktueller Stand, es funktioniert.

Das Installationsprogramm fordert irgendwann am Anfang die Eingabe der Lizenznummer. Dann wird die Lizenz vom WEB_Server heruntergeladen. Die Verbindung steht also zu dem Zeitpunkt schon. Normalerweise wird sie getrennt, und das Installationsprogramm fortgesetzt. Jetzt setze ich die Installation genauso fort, und starte den Thread.

Während der weiteren Installation (das Setup kopiert die Dateien usw.) lädt der Thread eventuell vorhandene aktuellere Dateien herunter.

Ist die Installation abgeschlossen, kann es im Großen und Ganzen nur zwei Zustände geben: Der Download-Thead arbeitet noch, oder ist schon fertig. Wenn er schon fertig ist, gibt (bzw. gab) es überhaupt keine Anforderung etwas zu synchronisieren, und wenn das Setup schon fertig ist und dann quasi auf den Thread warten muss...hat das Setup ja nichts mehr zu tun. Mit einem Timer frage ich dann die property "Progress" des Threads ab, aktualisiere den Progressbar in der MainForm, und warte bis der Thread fertig ist.

Exakt so verwendet dann später mein Update-Programm den Thread. Es sind darin nur noch ein paar Zeilen Code zu ändern. UI, Logik und Daten voneinander zu trennen hat sich wieder mal bewährt.

Das ist so einfach, dass ich mich im Nachhinein wundere, es nicht schon vorher so gemacht zu haben. Und die "Angst" vor Threads ist jetzt auch weg. Im Gegenteil, ich habe jetzt Lust auf mehr.

Zitat:

Die eigentliche Frage lauten: An welcher Stelle kommst Du nicht weiter?
Danke Mavarik, die Motivation es einfach noch mal zu versuchen, kam von dir.

Vielen Dank auch an alle die sich Mühe und Gedanken gemacht haben. Wie gesagt, es funzt momentan schon, aber ein paar kosmetische Kleinigkeiten sind noch zu erledigen. Und dann kommen ja noch ein paar neue Anforderungen auf mich zu.

Das Setup ist nämlich nicht nur für Installation und Deinstallation zuständig, auch für Datensicherung, Datenrücksicherung, PC-Umzug(Migration), Datenbankprüfung usw. Also noch genügend Potential für Threads, um zum Beispiel gleichzeitig mehrere Mandanten(-Datenbanken) zu sichern. BTW wenn es "nur" ein popeliges Installationsprogramm wäre, hätte ich es ja nicht selber geschrieben, sondern mit Inno gemacht.

dGeek 20. Dez 2014 20:00

AW: Update-Vorgang in einen Thread auslagern
 
Ich habe den Updatevorgang in meiner Software auch in einen Thread ausgelagert.

Ganz klarer Vorteil:
- man kann ungestört weiterarbeiten und braucht die Software nicht erst zu beenden um einen "Update-Checker" zu starten.
Nach erfolgreichem Download der Updates muss man logischerweise die Software neu starten und ein kleiner "Client", wenn man denn so programmiert hat, muss die Updates installieren.
Aber immer noch, für mich, die beste Lösung.

mm1256 20. Dez 2014 20:06

AW: Update-Vorgang in einen Thread auslagern
 
Zitat:

Zitat von dGeek (Beitrag 1284221)
...Nach erfolgreichem Download der Updates muss man logischerweise die Software neu starten und ein kleiner "Client", wenn man denn so programmiert hat, muss die Updates installieren.
Aber immer noch, für mich, die beste Lösung.

Nicht nur für dich :thumb: Ich mache den Check wenn der User meine SW beendet: "Es steht eine neuere Version zur Verfügung. Die neuen Programmdateien wurden bereits im Hintergrund heruntergeladen und werden nun aktiviert...". Das Updaten dauert dann nur noch Sekunden.

dGeek 20. Dez 2014 20:08

AW: Update-Vorgang in einen Thread auslagern
 
Sehr schön :thumb: Solche Software mag ich. Für mich ist Usability das fast Wichtigste einer Software.

Dejan Vu 21. Dez 2014 13:29

AW: Update-Vorgang in einen Thread auslagern
 
Da wird ein Update installiert, ohne mich zu fragen ??? Das ist aber benutzerfreundlich. ;-)

mm1256 21. Dez 2014 15:41

AW: Update-Vorgang in einen Thread auslagern
 
Liste der Anhänge anzeigen (Anzahl: 1)
Dich fragt ja auch keiner, oder hat keiner gefragt. Es ist vollkommend ausreichend, wenn ich meine Kunden um Erlaubnis frage.

Dejan Vu 21. Dez 2014 15:44

AW: Update-Vorgang in einen Thread auslagern
 
Zitat:

Zitat von mm1256 (Beitrag 1284302)
Dich fragt ja auch keiner, oder hat keiner gefragt. Es ist vollkommend ausreichend, wenn ich meine Kunden um Erlaubnis frage.

:lol: :thumb:

Pfaffe 21. Dez 2014 16:07

AW: Update-Vorgang in einen Thread auslagern
 
Vielleicht so für den Anwender auswählbar:
a. Updates automatisch suchen, herunterladen und installieren
b. Updates automatisch suchen herunterladen und manuell installieren
c. Updates automatisch suchen, manuell herunterladen und manuell installieren
d. Update manuell suchen. -> [Jetzt suchen]

Und in einem Firmennetz muss es möglich sein, dass der Domänen-Admin bestimmen kann, wie geupdatet wird. Das kann ein Auskriterium bei Firmen sein.

dGeek 21. Dez 2014 16:28

AW: Update-Vorgang in einen Thread auslagern
 
Zitat:

Zitat von Dejan Vu (Beitrag 1284292)
Da wird ein Update installiert, ohne mich zu fragen ??? Das ist aber benutzerfreundlich. ;-)

Meine Software bietet eine Auswahlmöglichkeit, um alle n-Tage automatisch nach Updates zu suchen. Die Updatesuche kann man auch komplett ausschalten und manuell suchen.
Es gibt auch eine Option, um Updates mit Bestätigung oder ohne, also Silent, zu installieren.

Usability ;) Damals wurde mir beigebracht, selbst die dümmsten Szenarien zu beachten. Denn es gibt nichts, vorsicht, "Dümmeres" als den Endbenutzer.

mm1256 21. Dez 2014 18:57

AW: Update-Vorgang in einen Thread auslagern
 
Hallo,

die angesprochenen Möglichkeiten gibt es natürlich. Meine Mitbewerber spielen ja fast ausnahmslos in der Champions-League-Größe mit über 20.000 Kunden, und da ist es manchmal nicht leichtr mitzuhalten. Der Kunde kann auswählen, ob er generell Updates installieren will, in welchem Intervall (wöchentlich, monatlich), an welchem Wochentag, und zu welchem Zeitpunkt. Diese Einstellungen gehören jedoch nicht in ein Installationsprogramm. Dafür gibt es die programminternen Systemeinstellungen mit einer Benutzerrechte-Verwaltung. Hier ist festgelegt, welcher Benutzer überhaupt an diesen Einstellungen Änderungen vornehmen darf. Der Benutzer, der das Update installiert, hat also nicht zwangsläufig auch das Recht, diese Einstellungen ändern zu dürfen.

In diesem Fall geht es mir im Speziellen darum, den Lebenszyklus einer CD von etwa einem Jahr zu optimieren bzw. zu gewährleisten. Wenn die CD's zum Jahreswechsel gepresst werden, dann sollen sie auch das ganze Jahr über verwendbar sein. Denn, es sind natürlich neben den Kunden auch potentielle Interessenten, welche die CD erhalten.

Mich ärgert in diesem Zusammenhang immer, wenn ich eine CD installiere, dass das installierte Programm beim bzw. nach dem ersten Start schon nach Updates sucht bzw. suchen muss. Zu diesem Zeitpunkt habe ich andere Vorstellungen und Interessen. Und zudem entsteht der Eindruck, dass die CD "veraltet" ist. Der erste Eindruck ist da schnell dahin.

Beim Software-Download (ESD) sieht das natürlich wieder ganz anders aus, aber der ESD-Anteil liegt bei mir bei unter 10%.

Dejan Vu 22. Dez 2014 08:57

AW: Update-Vorgang in einen Thread auslagern
 
Ich habe derartiges nie anbieten müssen, da die Software vom Kunden installiert wird, d.h. ich 'liefere' an die Haustür, auch Updates und Bugfixes und der Kunde bzw. mein single point of contact ist dann dafür verantwortlich, die Updates zu testen und zu deployen.

Nicht, das das eine bessere Möglichkeit wäre, ich kam nur nie in Verlegenheit, eine derartige Funktion implementieren zu müssen. Oder habe mich drum drücken können.

Mavarik 22. Dez 2014 17:23

AW: Update-Vorgang in einen Thread auslagern
 
Zitat:

Zitat von mm1256 (Beitrag 1284309)
Mich ärgert in diesem Zusammenhang immer, wenn ich eine CD installiere, dass das installierte Programm beim bzw. nach dem ersten Start schon nach Updates sucht bzw. suchen muss. Zu diesem Zeitpunkt habe ich andere Vorstellungen und Interessen. Und zudem entsteht der Eindruck, dass die CD "veraltet" ist. Der erste Eindruck ist da schnell dahin.

Und das klappt bei Dir? Respekt...

Die blöden Fehler rutschen doch bei allen Tests durch... Wenn nicht hätte man die ja schon beseitigt.
Wenn also die ersten 100 Kunden die neue CD installiert haben, gibt es doch schon das 1. Update oder?

Wer nein sagt werfe das 1. Byte...

Deswegen legen einige Kunden immer eine neue CD erst in die Schublade... Bis Version := Version + 0.01 da ist...

Mavarik

mm1256 22. Dez 2014 18:09

AW: Update-Vorgang in einen Thread auslagern
 
Hallo Frank,

Zitat:

Zitat von Mavarik
Wenn also die ersten 100 Kunden die neue CD installiert haben, gibt es doch schon das 1. Update oder?

Ich merke schon....du bist Realist. Sowas KANN mal passieren, da werfe ich nicht das erste Byte. Allerdings (klopf..klopf..klopf..auf Holz) im Januar 2011 das letzte mal.

Etwa 1/3 meiner Kunden installiert ein neues Update innerhalb der ersten Woche nach Erscheinen und etwa 1/10 sogar innerhalb der ersten zwei Tage. Wenn mir also wirklich ein Fehler durch die Lappen geht, weiß ich das i.d.R. schon in ein paar Tagen. Mit der jetzigen Lösung bedeutet das, dass etwa 2/3 gar nichts mitbekommen würden, und das beruhigt schon enorm.

Da half übrigens bisher auch das Update "nach" der Installation oft nichts, denn wir wissen ja, wo die User hinklicken sollen, und es trotzdem nicht tun. Ein WEB-Update braucht nun mal auch Admin-Rechte um Dateien überschreiben zu dürfen. Die Code-Signierung hat schon etwas gebracht, weil das blaue Fenster doch etwas vertrauenserweckender ist. Aber...

- Haben Sie das blaue Fenster mit dem Hinweis nicht gesehen?
- Doch, das habe ich immer weg gedrückt...mache ich immer so...es ging ja auch weiter... :cyclops: :cyclops:

Zitat:

Zitat von Mavarik
....Bis Version := Version + 0.01 da ist...

Diese "xxx.01" usw. finde ich psychologisch weniger sinnvoll und darum gibts das bei mir seit 2012 nicht mehr. Seitdem beinhaltet meine Versions-Nummer die Jahreszahl und am Schluss eine fortlaufende Build-Nummer. Ist mit 20.12.10101.1 los gegangen und derzeit bin ich bei 20.14.11210.18.

20.14.11210.18 entschlüsselt:

20.14 = Jahr
11210 = immer eine "1" und dann Monat und Tag des Build zweistellig, hier also der 10.12.2014
.18 = Fortlaufende Build-Nummer.

Die Versions-Nummer wird also permanent größer, und hat niemals irgendwas mit ".01" oder so. Und, es hilft, hat sich bestens bewährt :-D


Alle Zeitangaben in WEZ +1. Es ist jetzt 12:24 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz