Toon posts:

[Delphi] Programma dat zichzelf kan updaten (al draaiende)

Pagina: 1
Acties:
  • 135 views sinds 30-01-2008
  • Reageer

Verwijderd

Topicstarter
Door de updatefunctie in het programma µTorrent (http://www.utorrent.com/) kwam ik op het idee om een programma van mezelf uit te rusten met een snelle & eenvoudige updatefunctie. Het idee is als volgt: mijn programma bestaat uit één exefile waar alle functionaliteit in zit. In het programma zit een updatecontrole, die het versienummer van het draaiende programma vergelijkt met een nummer dat wordt uitgelezen vanaf een vaste url. Op deze manier weet het programma of er een update beschikbaar is.

Wanneer de gebruiker er voor kiest om de huidige versie te updaten, worden de volgende stappen uitgevoerd:
  1. De update wordt gedownload en opgeslagen als "programma.update"
  2. Het gedownloade bestand wordt gecontroleerd (grootte + checksum)
  3. Het draaiende programma hernoemt zichzelf van "programma.exe" -> "programma.old"
  4. Het draaiende programma hernoemt "programma.update" -> "programma.exe"
  5. Het draaiende programma sluit zichzelf af & start "programma.exe"
  6. De nieuwe versie ziet "programma.old" staan en verwijdert dit bestand
  7. Klaar!
Hierdoor is er geen extern 'updater'-bestand nodig en kan alles vanuit de eigen exefile gebeuren. De 'grote truc' zit hem in het feit dat een programma zichzelf kan hernoemen terwijl het draait. Dit verbaasde mij, maar blijkbaar is dit heel gewoon.

Zijn er mensen die iets soortgelijks hebben gedaan (evt. ook in Delphi) en die er een betere manier voor denken te hebben? Zie ik dingen over het hoofd die de boel in de war kunnen laten lopen?

Hier de belangrijkste delen code die ik heb gebruikt:
Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// Deze procedure uitvoeren wanneer de gebruiker wil updaten
// en de update is gedownload & gecontroleerd
procedure updateCurrentExecutable();
begin
  if (FileExists('programma.update')) then
  begin
    RenameFile('programma.exe', 'programma.old');
    RenameFile('programma.update', 'programma.exe');
  end;
end;

// Deze procedure altijd uitvoeren bij de start van het programma
procedure deleteOldVersion();
begin
  if (FileExists('programma.old')) then
    DeleteFile('programma.old');
end;

// Deze code staat in het .dpr bestand (de main loop van het programma)
Application.Initialize;
Application.Title := 'Programma';
Application.CreateForm(TfrmProgrammaMain, frmProgrammaMain);
// In de volgende regel wordt de main loop gestart
Application.Run;
// Als we hier zijn aangekomen, is het programma 'afgesloten'

// FRestartApplication is een globale flag die op True wordt gezet
// wanneer een update is gedownload & gecontroleerd
if (FRestartApplication) then
begin
  si.cb := sizeof(STARTUPINFO);
  CreateProcess('programma.exe', nil, nil, nil, FALSE, 0, nil, nil, si, pi);
 
  CloseHandle(pi.hProcess);
  CloseHandle(pi.hThread);
end;

  • LordLarry
  • Registratie: Juli 2001
  • Niet online

LordLarry

Aut disce aut discede

Alleen getest onder XP? Volgens mij werkt dit namelijk niet bij andere windows versies. De meeste zelf updaters generen een bat bestandje of extraheren een exe uit een resource die het renamen doet nadat de applicatie zichzelf gesloten heeft.

We adore chaos because we like to restore order - M.C. Escher


  • Dunka
  • Registratie: Augustus 2005
  • Laatst online: 22-04 12:01
Dit zal ook niet werken als de gebruiker onvoldoende rechten (schrijven/wijzigen) heeft op de map waarin het programmabestand staat.

Verwijderd

Vraag me idd ook af of het hernoemen van het huidige proces bestand altijd goed gaat.

Een andere optie die ook niet verkeerd is. En ranzige constructies vermijd is het volgende...
(zelf ff checken of het op alle besturingssystemen werkt.)
  1. Nadat je programma.update hebt gedownload voer je deze uit met CreateProcess. Het is niet nodig om programma op .exe te laten eindigen :)
  2. update procedure alleen uitvoeren als bestandsnaam 'programma.update' is.
  3. Laat programma.update wachten totdat de originele programma.exe afgesloten is. Bijvoorbeeld door de pid als parameter mee te geven. Of d.m.v. ipc communicatie met hoofdprogramma. (Pas op voor deadlocks!! Timeout inbouwen ofzo...)
  4. programma.exe is nu niet meer in gebruik. Laat programma.update zichzelf kopieren naar programma.exe
  5. Laat programma.update zichzelf verwijderen. of laat programma.exe het bestand programma.update verwijderen als deze gelijk is aan zichzelf.

[ Voor 4% gewijzigd door Verwijderd op 24-11-2005 18:37 ]


Verwijderd

Topicstarter
Dunka: Dit zal ook niet werken als de gebruiker onvoldoende rechten (schrijven/wijzigen) heeft op de map waarin het programmabestand staat.
Dat klopt, maar voor het updaten zal het programmabestand toch op de één of andere manier moeten worden vervangen. Dus dat moet altijd door iemand gebeuren die die schrijfrechten heeft.
LordLarry: Alleen getest onder XP? Volgens mij werkt dit namelijk niet bij andere windows versies. De meeste zelf updaters generen een bat bestandje of extraheren een exe uit een resource die het renamen doet nadat de applicatie zichzelf gesloten heeft.
Ik heb dit idee alleen getest op de machine waar ik op ontwikkel en die draait inderdaad XP. Maar weet je toevallig ook welk deel van mijn code de problemen op andere versies zou moeten veroorzaken, bv. het renamen van het exebestand dat is gebruikt om het huidige proces te starten?
sinaasappelsap:
  1. programma.exe is nu niet meer in gebruik. Laat programma.update zichzelf kopieren naar programma.exe
  2. Laat programma.update zichzelf verwijderen. of laat programma.exe het bestand programma.update verwijderen als deze gelijk is aan zichzelf.
Zou ik deze stappen niet kunnen vervangen door het truukje met het hernoemen van het draaiende programma? Edit: Nee dus, als de informatie van LordLarry in de post hier onder correct is. Misschien dat ik de optie via de batch-file ook maar eens ga bekijken, alhoewel je dan dus wel via externe files dingen gaat doen.

Ik ga het idee van sinaasappelsap binnenkort uitwerken met hier en daar misschien een kleine wijziging. Het resultaat laat ik hier uiteraard zien. Bedankt voor jullie ideeën tot nu toe! (Dit wil trouwens niet zeggen dat je hier nu niet meer hoeft te reageren, suggesties zijn altijd welkom).

[ Voor 7% gewijzigd door Verwijderd op 24-11-2005 23:09 . Reden: Aanvulling vanwege nieuwe informatie van LordLarry ]


  • LordLarry
  • Registratie: Juli 2001
  • Niet online

LordLarry

Aut disce aut discede

Verwijderd schreef op donderdag 24 november 2005 @ 19:43:
Ik heb dit idee alleen getest op de machine waar ik op ontwikkel en die draait inderdaad XP. Maar weet je toevallig ook welk deel van mijn code de problemen op andere versies zou moeten veroorzaken, bv. het renamen van het exebestand dat is gebruikt om het huidige proces te starten?
Het renamen en/of verwijderen van reeds draaiende EXEs en DLLs.

We adore chaos because we like to restore order - M.C. Escher


Verwijderd

Topicstarter
Een alternatief voor de aanpak waarin alle update-code in het programma zelf zit, is het gebruik maken van een batch-file die het lastige werk opknapt. Batchfiles kunnen zichzelf namelijk wel verwijderen, ook onder oudere Windows-versies (zie http://www.catch22.net/tuts/selfdel.asp)

De inhoud van de batchfile ziet er dan ongeveer zo uit:
code:
1
2
3
4
5
6
7
8
9
10
11
12
@ECHO OFF
@IF NOT EXIST C:\temp\prog.update GOTO terminate

:dodelete
@DEL C:\Progra~1\Applic\prog.exe
@IF EXIST C:\Progra~1\Applic\prog.exe GOTO dodelete

@MOVE /Y C:\temp\prog.update C:\Progra~1\Applic\prog.exe
@START /B C:\Progra~1\Applic\prog.exe

:terminate
@DEL "C:\temp\update.bat"


Deze batch-file voert de volgende stappen uit:
  1. Controleer of er een update-bestand aanwezig is, zo niet, stop direct
  2. Blijf proberen het exebestand te verwijderen (kan even duren voor het programma daadwerkelijk is afgesloten, maar ondertussen is de batchfile natuurlijk al gestart
  3. Verplaats de file met de nieuwe versie naar de programmamap onder de nieuwe naam
  4. Start het programma
  5. Verwijder de batchfile (hiermee verwijdert de file zichzelf dus)
Deze batchfile kan worden gemaakt & gestart vanuit het oorspronkelijke programma. Waarschijnlijk lukt het ook wel om dit te doen zonder dat er heel kort het zwarte commandoprompt-scherm zichtbaar is.

  • epic007
  • Registratie: Februari 2004
  • Laatst online: 11-03 09:21
Ik zit zelf aan het volgende te denken:

een minimale .exe, het enige wat deze doet is het opstarten van een main.dll wat eigenlijk je applicatie is. Bij een update wordt de dll afgesloten en de exe verzorgt de update en start vervolgens de dll weer op.
Pagina: 1