Inno Setup: come disinstallare automaticamente la versione precedente installata?


88

Sto usando Inno Setup per creare un programma di installazione.

Voglio che il programma di installazione disinstalli automaticamente la versione installata precedente, invece di sovrascriverla. Come posso fare ciò?


2
Nota che, come ha detto mlaan, normalmente non è necessario farlo con una configurazione basata su Inno a meno che tu non stia aggiornando da una versione non Inno.
Deanna

7
Deanna: dipende dal caso. Per alcuni programmi con sistemi di plugin automatici, che leggono qualsiasi cosa in una cartella, la rimozione di vecchi file è un must assoluto quando si installa una nuova versione, e semplicemente eseguire la disinstallazione è di solito il modo più pulito per farlo.
Nyerguds

1
@Nyerguds Ma InnoSetup soddisfa questo problema avendo un'opzione per eliminare determinati file / cartelle prima dell'inizio dell'installazione (flag "InstallDelete") in modo da non dover disinstallare prima la vecchia versione.
NickG

3
@ NickG: ancora una volta, dipende dal caso. Questa sarebbe la situazione ideale, sì, e di gran lunga quella preferita, ma in realtà ci sono molte situazioni non ideali. Uno di questi esempi sono i file dll registrati, su molte possibili versioni di destinazione.
Nyerguds

Risposte:


27

Dovresti essere in grado di leggere la stringa di disinstallazione dal registro, dato l'AppId (ovvero il valore utilizzato AppIDnella [Setup]sezione-). Potrebbe essere trovato sotto Software\Microsoft\Windows\CurrentVersion\Uninstall\{AppId}\(potrebbe essere HKLMo HKCU, quindi è meglio controllare entrambi) dove {AppId}dovrebbe essere sostituito con il valore effettivo che hai usato. Cerca i valori UninstallStringo QuietUninstallStringe utilizza la Execfunzione per eseguirlo dalla InitializeSetup()funzione evento.

Aggiornamento: rimossa la soluzione alternativa non funzionante utilizzando una [Run]voce di sezione con {uninstallexe}- grazie a tutti i commentatori che lo hanno segnalato!


+1, ma usa sicuramente lo scripting per leggere il vecchio nome del programma di disinstallazione, perché altrimenti non funzionerà se l'utente ha inserito un percorso diverso.
mghie

3
Non penso che la [Run]soluzione della sezione funzionerà, perché viene eseguita troppo tardi nel processo di installazione. Dal manuale di Inno Setup: La sezione [Esegui] è opzionale e specifica un numero qualsiasi di programmi da eseguire dopo che il programma è stato installato con successo, ma prima che il programma di installazione visualizzi la finestra di dialogo finale.
Craig McQueen

Modifica questo post e rimuovi la parte [Esegui], non funziona. La seconda parte funziona però. Grazie :-)
Saulius Žemaitaitis

11
Un avvertimento: per un'applicazione a 32 bit su Windows a 64 bit, il percorso potrebbe essere "Software \ Wow6432Node \ Microsoft \ Windows \ CurrentVersion \ Uninstall \ {AppId}"
Adrian Cox

4
@ Adrian: Anche se questo potrebbe essere vero a livello fisico, penso che le chiamate WinAPI usate da Inno se ne occuperanno già - almeno se lo stesso setup.exe è un processo a 32 bit.
Oliver Giesen

112

Ho usato quanto segue. Non sono sicuro che sia il modo più semplice per farlo, ma funziona.

Questo utilizza {#emit SetupSetting("AppId")}che si basa sul Preprocessore Inno Setup. Se non lo usi, copia e incolla direttamente il tuo ID app.

[Code]

{ ///////////////////////////////////////////////////////////////////// }
function GetUninstallString(): String;
var
  sUnInstPath: String;
  sUnInstallString: String;
begin
  sUnInstPath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{#emit SetupSetting("AppId")}_is1');
  sUnInstallString := '';
  if not RegQueryStringValue(HKLM, sUnInstPath, 'UninstallString', sUnInstallString) then
    RegQueryStringValue(HKCU, sUnInstPath, 'UninstallString', sUnInstallString);
  Result := sUnInstallString;
end;


{ ///////////////////////////////////////////////////////////////////// }
function IsUpgrade(): Boolean;
begin
  Result := (GetUninstallString() <> '');
end;


{ ///////////////////////////////////////////////////////////////////// }
function UnInstallOldVersion(): Integer;
var
  sUnInstallString: String;
  iResultCode: Integer;
begin
{ Return Values: }
{ 1 - uninstall string is empty }
{ 2 - error executing the UnInstallString }
{ 3 - successfully executed the UnInstallString }

  { default return value }
  Result := 0;

  { get the uninstall string of the old app }
  sUnInstallString := GetUninstallString();
  if sUnInstallString <> '' then begin
    sUnInstallString := RemoveQuotes(sUnInstallString);
    if Exec(sUnInstallString, '/SILENT /NORESTART /SUPPRESSMSGBOXES','', SW_HIDE, ewWaitUntilTerminated, iResultCode) then
      Result := 3
    else
      Result := 2;
  end else
    Result := 1;
end;

{ ///////////////////////////////////////////////////////////////////// }
procedure CurStepChanged(CurStep: TSetupStep);
begin
  if (CurStep=ssInstall) then
  begin
    if (IsUpgrade()) then
    begin
      UnInstallOldVersion();
    end;
  end;
end;

Alternative

Vedi anche questo post del blog "Inno Setup Script Sample for Version Comparison" che fa un ulteriore passo avanti e legge il numero di versione di qualsiasi versione precedentemente installata e confronta quel numero di versione con quello del pacchetto di installazione corrente.


3
grazie per aver fatto riferimento al mio post sul blog. L'esempio completo di quel post è disponibile qui, code.google.com/p/lextudio/source/browse/trunk/trunk/setup/…
Lex Li

L'unica cosa che manca qui è il supporto per una voce di disinstallazione sotto HKCU invece di HKLM.
Oliver Giesen

1
Posso suggerire di aggiungere la possibilità di disinstallare se qualsiasi utente ha installato l'applicazione, in particolare se l'utente corrente è un amministratore? ... UserSIDs: TArrayOfString; I: Integer; ... if not RegQueryStringValue(HKCU, sUnInstPath, 'UninstallString', sUnInstallString) then if isAdminLoggedOn() and RegGetSubkeyNames( HKEY_USERS, '', UserSIDs ) then for I := 0 to GetArrayLength( UserSIDs ) - 1 do begin if RegQueryStringValue( HKEY_USERS, UserSIDs[I] + '\' + sUnInstPath, 'UninstallString', sUnInstallString ) then break; end;
Terrance

2
Ottima soluzione, funziona bene. Tuttavia apre una finestra durante l'installazione che mostra "Disinstallazione di [nome software]". È possibile impedire che questa finestra si apra? Perché l'installazione del mio software è così veloce che la finestra di installazione si chiude prima della finestra di disinstallazione e sembra strano ...
André Santaló

4
@ AndréSantaló Usa / VERYSILENT invece di / SILENT
Gautam Jain

7

Se "vuoi solo rimuovere le vecchie icone" (perché le tue sono cambiate / aggiornate) puoi usare questo:

; attempt to remove previous versions' icons
[InstallDelete]
Type: filesandordirs; Name: {group}\*;

Viene eseguito "all'inizio dell'installazione", quindi in pratica rimuove le vecchie icone e quelle nuove saranno ancora installate lì dopo che questo è stato completamente fatto.

Lo faccio con ogni installazione "nel caso in cui qualcosa sia cambiato" per quanto riguarda l'icona (tutto viene reinstallato comunque).


Se hai un aggiornamento per le tue icone, lascia che siano sovrascritte. Non è necessario rimuoverli. Bene, se vuoi rimuoverli, puoi usare questa opzione. Questo è il modo corretto. In ogni caso, il ragazzo con cui stavi parlando (mlaan, Martijn Laan) è l'autore di Inno Setup e penso che sappia di cosa sta parlando :-)
TLama

1
Sì, è quando vuoi rinominare o spostare un'icona che ne hai bisogno. Ad esempio, se v5 ne ha uno chiamato "run" e v6 lo ha rinominato in "run basic" se un utente installa v5 e poi v6, si ritroveranno con 2 icone, quando in realtà ne volevi 1 ("run basic"). Quindi questo trucco finisce per essere necessario (@mlaan +1 per modificare il comportamento predefinito di innosetup per rimuovere le vecchie icone e non è necessario questo ...)
rogerdpack

6

Quando si utilizza Inno Setup, non c'è motivo di disinstallare una versione precedente a meno che quella versione non sia stata installata da un programma di installazione diverso. In caso contrario, gli aggiornamenti vengono gestiti automaticamente.


6
Il nostro programma ha subito un cambiamento nella struttura, quindi la vecchia versione deve essere disinstallata.
Quan Mai,

11
No, è possibile aggiungere voci allo script per gestire la modifica della struttura durante un aggiornamento.
mlaan

@mlaan E quali sarebbero quelle voci?
Mythofechelon

1
Potresti semplicemente usare una [InstallDelete]sezione per rimuovere vecchi file / directory. I nuovi file verranno quindi inseriti nelle posizioni corrette durante l'installazione.
daiscog

2
Se aggiorni una libreria di terze parti come DevExpress, che ha numeri di versione nei nomi delle DLL (come 15.1 installata prima e 15.2 ora), allora vuoi rimuovere la vecchia versione. IMHO questa è una buona ragione.
Thomas Weller

2

La risposta fornita da Craig McQueen è totalmente fattibile. Tuttavia, aggiungerei questi commenti:

  • Il {#emit SetupSetting("AppId")}codice non funziona per me, quindi aggiungo semplicemente il mio ID app.
  • Non volevo eseguire il mio programma di disinstallazione, perché ho un file di configurazione INI memorizzato nella cartella AppData / che viene rimosso dal programma di disinstallazione e non voglio che venga cancellato durante l'installazione di una nuova versione. Quindi, ho modificato un po 'il codice fornito da Craig McQueen per rimuovere la directory in cui è installato il programma, dopo aver recuperato il suo percorso.

Quindi, per quanto riguarda il codice di Craig McQueen, le modifiche sono:

  • Recupera la InstallLocationchiave invece della UninstallStringchiave.
  • Usa la DelTreefunzione invece diExec(sUnInstallString, ...)

1

Per chiunque utilizzi quanto GetUninstallString()suggerito sopra per forzare una disinstallazione interna CurStepChanged()e ha problemi di cache del disco, vedere di seguito una soluzione correlata che in realtà attende un po 'dopo la disinstallazione per l'eliminazione dell'exe del programma di disinstallazione!

Problema di cache del disco con inno-setup?


0

Puoi eseguire un programma di disinstallazione nella sezione [codice]. Devi capire come ottenere il percorso del programma di disinstallazione esistente. Per semplicità quando installo le mie app aggiungo un valore di stringa di registro che punta alla cartella contenente il programma di disinstallazione ed eseguo semplicemente il programma di disinstallazione nel callback InitializeWizard.

Tieni presente che i nomi del programma di disinstallazione di Inno Setup sono tutti nella forma uninsnnn.exe, devi tenerne conto nel tuo codice.


0

ho modificato il codice @Crain Mc-Queen, penso che questo codice sia migliore perché non è necessario modificarlo in un progetto diverso:

[Code]
function GetNumber(var temp: String): Integer;
var
  part: String;
  pos1: Integer;
begin
  if Length(temp) = 0 then
  begin
    Result := -1;
    Exit;
  end;
    pos1 := Pos('.', temp);
    if (pos1 = 0) then
    begin
      Result := StrToInt(temp);
    temp := '';
    end
    else
    begin
    part := Copy(temp, 1, pos1 - 1);
      temp := Copy(temp, pos1 + 1, Length(temp));
      Result := StrToInt(part);
    end;
end;

function CompareInner(var temp1, temp2: String): Integer;
var
  num1, num2: Integer;
begin
    num1 := GetNumber(temp1);
  num2 := GetNumber(temp2);
  if (num1 = -1) or (num2 = -1) then
  begin
    Result := 0;
    Exit;
  end;
      if (num1 > num2) then
      begin
        Result := 1;
      end
      else if (num1 < num2) then
      begin
        Result := -1;
      end
      else
      begin
        Result := CompareInner(temp1, temp2);
      end;
end;

function CompareVersion(str1, str2: String): Integer;
var
  temp1, temp2: String;
begin
    temp1 := str1;
    temp2 := str2;
    Result := CompareInner(temp1, temp2);
end;

function InitializeSetup(): Boolean;
var
  oldVersion: String;
  uninstaller: String;
  ErrorCode: Integer;
  vCurID      :String;
  vCurAppName :String;
begin
  vCurID:= '{#SetupSetting("AppId")}';
  vCurAppName:= '{#SetupSetting("AppName")}';
  //remove first "{" of ID
  vCurID:= Copy(vCurID, 2, Length(vCurID) - 1);
  //
  if RegKeyExists(HKEY_LOCAL_MACHINE,
    'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\' + vCurID + '_is1') then
  begin
    RegQueryStringValue(HKEY_LOCAL_MACHINE,
      'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\' + vCurID + '_is1',
      'DisplayVersion', oldVersion);
    if (CompareVersion(oldVersion, '{#SetupSetting("AppVersion")}') < 0) then      
    begin
      if MsgBox('Version ' + oldVersion + ' of ' + vCurAppName + ' is already installed. Continue to use this old version?',
        mbConfirmation, MB_YESNO) = IDYES then
      begin
        Result := False;
      end
      else
      begin
          RegQueryStringValue(HKEY_LOCAL_MACHINE,
            'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\' + vCurID + '_is1',
            'UninstallString', uninstaller);
          ShellExec('runas', uninstaller, '/SILENT', '', SW_HIDE, ewWaitUntilTerminated, ErrorCode);
          Result := True;
      end;
    end
    else
    begin
      MsgBox('Version ' + oldVersion + ' of ' + vCurAppName + ' is already installed. This installer will exit.',
        mbInformation, MB_OK);
      Result := False;
    end;
  end
  else
  begin
    Result := True;
  end;
end;

-1

Devo essermi perso qualcosa. I nuovi file vengono copiati nella directory di destinazione prima che si verifichi la rimozione della vecchia installazione. Quindi arriva il programma di disinstallazione che li elimina e rimuove la directory.


2
Non sono sicuro di cosa stai cercando di dire, ma tieni presente che non si tratta sempre solo di copiare file. Immagina di aver installato il tuo prodotto, che con la prossima versione viene fornito con una struttura di file completamente modificata, in cui molti dei file originali sono stati rimossi e i nuovi file hanno nomi diversi e sono memorizzati in directory diverse. Quale sarebbe il modo più semplice per eseguire l'aggiornamento? Non sarebbe disinstallare la versione precedente?
TLama

Uso INNO per installare un driver e le sue applicazioni. Naturalmente, l'effettiva installazione / rimozione del driver non viene eseguita direttamente da INNO. Piuttosto, INNO copia un'app di installazione / rimozione dei driver e quindi la esegue. Disinstallazione eseguita in modo simile: INNO esegue il dispositivo di rimozione del driver, quindi elimina i file.
Shaul

-8

Non utilizzare la sezione [Esegui], ma [UninstallRun]. Infatti, i programmi sotto [Esegui] vengono eseguiti dopo l'installazione, provocando la disinstallazione del programma immediatamente dopo l'installazione: - | La sezione [UninstallRun] viene invece valutata prima dell'installazione.


3
[UninstallRun]non è una soluzione per la domanda.
Craig McQueen

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.