Inno Setup: Wie deinstalliere ich die zuvor installierte Version automatisch?

86

Ich verwende Inno Setup, um ein Installationsprogramm zu erstellen.

Ich möchte, dass das Installationsprogramm die zuvor installierte Version automatisch deinstalliert, anstatt sie zu überschreiben. Wie kann ich das machen?

Quan Mai
quelle
2
Beachten Sie, dass, wie mlaan sagte, dies normalerweise mit einem Inno-basierten Setup nicht erforderlich ist , es sei denn, Sie führen ein Upgrade von einer Nicht-Inno-Version durch.
Deanna
6
Deanna: Das hängt vom Fall ab. Bei einigen Programmen mit automatischen Plugin-Systemen, die alles in einem Ordner lesen, ist das Entfernen alter Dateien bei der Installation einer neuen Version ein absolutes Muss. Das einfache Ausführen der Deinstallation ist normalerweise der sauberste Weg, dies zu tun.
Nyerguds
1
@Nyerguds InnoSetup bietet jedoch die Möglichkeit, bestimmte Dateien / Ordner vor Beginn der Installation zu löschen (Flag "InstallDelete"), sodass Sie die alte Version immer noch nicht deinstallieren müssen.
NickG
3
@ NickG: Auch hier kommt es auf den Fall an. Das wäre die ideale Situation, ja, und bei weitem die bevorzugte, aber in Wirklichkeit gibt es ziemlich viele nicht ideale Situationen. Ein solches Beispiel sind registrierte DLL-Dateien auf vielen möglichen Zielversionen.
Nyerguds

Antworten:

26

Sie sollten in der Lage sein, die Deinstallationszeichenfolge anhand der AppId (dh des Werts, für den Sie AppIDim [Setup]Abschnitt -s verwendet haben) aus der Registrierung zu lesen . Es könnte unter Software\Microsoft\Windows\CurrentVersion\Uninstall\{AppId}\(könnte entweder HKLModer sein HKCU, also überprüfen Sie am besten beide) gefunden werden, wo {AppId}es durch den tatsächlich verwendeten Wert ersetzt werden sollte. Suchen Sie nach den Werten UninstallStringoder QuietUninstallStringund verwenden Sie die ExecFunktion, um sie über Ihre InitializeSetup()Ereignisfunktion auszuführen .

Update: Nicht funktionierende alternative Lösung mit einem [Run]-section-Eintrag mit entfernt {uninstallexe}- danke an alle Kommentatoren, die darauf hingewiesen haben!

Oliver Giesen
quelle
+1, aber verwenden Sie auf jeden Fall Skripte, um den alten Namen des Deinstallationsprogramms zu lesen, da dies sonst nicht funktioniert, wenn der Benutzer einen anderen Pfad eingegeben hat.
mghie
3
Ich denke nicht, dass die [Run]Abschnittslösung funktionieren wird, da sie im Installationsprozess zu spät ausgeführt wird. Aus dem Inno Setup-Handbuch: Der Abschnitt [Ausführen] ist optional und gibt eine beliebige Anzahl von Programmen an, die ausgeführt werden sollen, nachdem das Programm erfolgreich installiert wurde, aber bevor das Setup-Programm den letzten Dialog anzeigt.
Craig McQueen
Bitte bearbeiten Sie diesen Beitrag und entfernen Sie den Teil [Ausführen], er funktioniert nicht. Der zweite Teil funktioniert jedoch. Vielen Dank :-)
Saulius Žemaitaitis
11
Eine Warnung: Für eine 32-Bit-Anwendung unter 64-Bit-Windows lautet der Pfad möglicherweise "Software \ Wow6432Node \ Microsoft \ Windows \ CurrentVersion \ Uninstall \ {AppId}"
Adrian Cox,
4
@Adrian: Während dies auf der physischen Ebene durchaus zutreffen mag, denke ich, dass die von Inno verwendeten WinAPI-Aufrufe dies bereits erledigen werden - zumindest wenn die setup.exe selbst ein 32-Bit-Prozess ist.
Oliver Giesen
110

Ich habe folgendes verwendet. Ich bin mir nicht sicher, ob dies der einfachste Weg ist, aber es funktioniert.

Dies verwendet, {#emit SetupSetting("AppId")}was auf dem Inno Setup Preprocessor beruht. Wenn Sie das nicht verwenden, schneiden Sie Ihre App-ID direkt aus und fügen Sie sie ein.

[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;

Alternativen

Siehe auch diesen Blog-Beitrag "Inno Setup Script Sample for Version Comparison", der einen Schritt weiter geht und die Versionsnummer einer zuvor installierten Version liest und diese Versionsnummer mit der des aktuellen Installationspakets vergleicht.

Craig McQueen
quelle
2
Vielen Dank für den Hinweis auf meinen Blog-Beitrag. Das vollständige Beispiel für diesen Beitrag finden Sie hier: code.google.com/p/lextudio/source/browse/trunk/trunk/setup/…
Lex Li
Das einzige, was hier fehlt, ist die Unterstützung eines Deinstallationseintrags unter HKCU anstelle von HKLM.
Oliver Giesen
1
Darf ich vorschlagen, die Möglichkeit zur Deinstallation hinzuzufügen, wenn ein Benutzer die Anwendung installiert hat, insbesondere wenn der aktuelle Benutzer ein Administrator ist? ... 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
Tolle Lösung, funktioniert gut. Während der Installation wird jedoch ein Fenster mit der Meldung "Deinstallation [Name der Software]" geöffnet. Kann verhindert werden, dass dieses Fenster platzt? Weil die Installation meiner Software so schnell ist, dass das Installationsfenster vor dem Deinstallationsfenster geschlossen wird und es seltsam aussieht ...
André Santaló
2
@ AndréSantaló Benutze / VERYSILENT anstelle von / SILENT
Gautam Jain
6

Bei Verwendung von Inno Setup gibt es keinen Grund, eine frühere Version zu deinstallieren, es sei denn, diese Version wurde von einem anderen Installationsprogramm installiert. Andernfalls werden Upgrades automatisch durchgeführt.

mlaan
quelle
6
Unser Programm hat sich in der Struktur geändert, daher muss die alte Version deinstalliert werden.
Quan Mai
11
Nein, dies ist nicht der Fall. Sie können Ihrem Skript Einträge hinzufügen, um die Strukturänderung während eines Updates zu behandeln.
Mlaan
@mlaan Und welche Einträge wären das?
Mythofechelon
1
Sie können einfach einen [InstallDelete]Abschnitt verwenden, um alte Dateien / Verzeichnisse zu entfernen. Die neuen Dateien werden dann während der Installation an den richtigen Speicherorten abgelegt.
Daiscog
2
Wenn Sie eine Drittanbieter-Bibliothek wie DevExpress aktualisieren, deren Versionsnummern in DLL-Namen enthalten sind (z. B. zuvor installierte 15.1 und jetzt 15.2), möchten Sie die alte Version entfernen. IMHO ist das ein guter Grund.
Thomas Weller
6

Wenn Sie "nur die alten Symbole entfernen möchten" (weil Ihre geändert / aktualisiert wurden), können Sie Folgendes verwenden:

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

Dies wird "zu Beginn der Installation" ausgeführt, sodass im Grunde genommen die alten Symbole entfernt werden und Ihre neuen Symbole dort weiterhin installiert werden, nachdem dies vollständig abgeschlossen ist.

Ich mache das einfach bei jeder Installation "falls sich etwas geändert hat" symbolisch (alles wird sowieso neu installiert).

Rogerdpack
quelle
Wenn Sie ein Update für Ihre Symbole haben, lassen Sie diese einfach überschreiben. Sie müssen nicht entfernt werden. Wenn Sie sie entfernen möchten, können Sie diese Option verwenden. Das ist richtig. Wie auch immer, der Typ, mit dem Sie gesprochen haben (mlaan, Martijn Laan), ist der Inno Setup-Autor und ich denke, er weiß, wovon er spricht :-)
TLama
1
Ja, wenn Sie ein Symbol umbenennen oder verschieben möchten, benötigen Sie dies. Wenn zum Beispiel v5 einen Namen mit dem Namen "run" hat und v6 ihn in "run basic" umbenannt hat, wenn ein Benutzer v5 und dann v6 installiert, werden 2 Symbole angezeigt, wenn Sie wirklich 1 wollten ("run basic"). Dieser Trick ist also notwendig (@mlaan +1, um das Standardverhalten von innosetup so zu ändern, dass alte Symbole entfernt werden und nicht benötigt werden ...)
rogerdpack
2

Die Antwort von Craig McQueen ist absolut realisierbar. Obwohl ich diese Kommentare hinzufügen würde:

  • Das {#emit SetupSetting("AppId")} Code funktioniert bei mir nicht, daher füge ich einfach meine App-ID hinzu.
  • Ich wollte mein Deinstallationsprogramm nicht ausführen, da ich eine INI-Konfigurationsdatei im Ordner AppData / gespeichert habe, die vom Deinstallationsprogramm entfernt wird, und ich möchte nicht, dass sie bei der Installation einer neuen Version gelöscht wird. Also habe ich den von Craig McQueen bereitgestellten Code ein wenig geändert, um das Verzeichnis zu entfernen, in dem das Programm installiert ist, nachdem ich seinen Pfad abgerufen habe.

In Bezug auf den Code von Craig McQueen gibt es folgende Änderungen:

  • Rufen Sie den InstallLocationSchlüssel anstelle des abUninstallString Schlüssels ab.
  • Verwenden Sie die DelTreeFunktion anstelle vonExec(sUnInstallString, ...)

quelle
1

Für alle, die die GetUninstallString()oben vorgeschlagenen CurStepChanged()Methoden verwenden , um eine Deinstallation im Inneren zu erzwingen, und Probleme beim Zwischenspeichern von Datenträgern auftreten, finden Sie unten eine verwandte Lösung, die nach der Deinstallation tatsächlich eine Weile auf das Löschen der Deinstallations-Exe wartet!

Festplatten-Caching-Problem mit Inno-Setup?

Fubar
quelle
0

Sie können ein Deinstallationsprogramm im Abschnitt [Code] ausführen. Sie müssen herausfinden, wie Sie den Pfad zum vorhandenen Deinstallationsprogramm erhalten. Der Einfachheit halber füge ich bei der Installation meiner Apps einen Registrierungszeichenfolgenwert hinzu, der auf den Ordner mit dem Deinstallationsprogramm verweist, und führe das Deinstallationsprogramm einfach im InitializeWizard-Rückruf aus.

Beachten Sie, dass die Namen der Inno-Setup-Deinstallationsprogramme alle die Form uninsnnn.exe haben. Sie müssen dies in Ihrem Code berücksichtigen.

Jim in Texas
quelle
0

Ich habe @Crain Mc-Queen Code bearbeitet, ich denke, dieser Code ist besser, weil er nicht in einem anderen Projekt geändert werden muss:

[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;
MohsenB
quelle
-1

Mir muss etwas fehlen. Die neuen Dateien werden in das Zielverzeichnis kopiert, bevor die alte Installation entfernt wird. Dann kommt das Deinstallationsprogramm, löscht sie und entfernt das Verzeichnis.

Shaul
quelle
2
Ich bin mir nicht sicher, was Sie sagen wollen, aber beachten Sie, dass es nicht immer nur um das Kopieren von Dateien geht. Stellen Sie sich vor, Sie hätten Ihr Produkt installiert, das mit der nächsten Version eine völlig geänderte Dateistruktur aufweist, bei der viele der Originaldateien entfernt wurden und neue Dateien unterschiedliche Namen haben und in unterschiedlichen Verzeichnissen gespeichert werden. Was wäre der einfachste Weg, um ein Upgrade durchzuführen? Wäre das nicht eine Deinstallation der vorherigen Version?
TLama
Ich verwende INNO, um einen Treiber und die zugehörigen Anwendungen zu installieren. Natürlich wird die eigentliche Installation / Entfernung des Treibers nicht direkt von INNO durchgeführt. Vielmehr kopiert INNO eine Treiberinstallations- / Entferner-App und führt sie dann aus. Die Deinstallation erfolgt ähnlich: INNO führt den Treiberentferner aus und löscht dann Dateien.
Shaul
-8

Verwenden Sie nicht den Abschnitt [Ausführen], sondern den Abschnitt [UninstallRun]. Tatsächlich wird das Programm unter [Ausführen] nach der Installation ausgeführt, wodurch Ihr Programm unmittelbar nach der Installation deinstalliert wird: - | Stattdessen wird der Abschnitt [UninstallRun] vor der Installation ausgewertet .

Andrea Ferroni alias bubbakk
quelle
3
[UninstallRun]ist keine Lösung für die Frage.
Craig McQueen
-8

Folgen Sie diesem Link: http://news.jrsoftware.org/news/innosetup/msg55323.html

In der Funktion InitializeSetup () können Sie nach der Eingabeaufforderung des Benutzers "MSIEXEC / x {Ihre Programm-ID}" aufrufen, um die alte Version zu deinstallieren

Tonny Nguyen
quelle
5
MSIEXEC funktioniert nur für MSI-Pakete. Dies gilt nicht für Inno Setup.
Lex Li