Inno Setup: Comment désinstaller automatiquement la version précédente installée?


88

J'utilise Inno Setup pour créer un installateur.

Je souhaite que le programme d'installation désinstalle automatiquement la version précédente installée, au lieu de l'écraser. Comment puis je faire ça?


2
Notez que comme mlaan l'a dit, il n'est normalement pas nécessaire de le faire avec une configuration basée sur Inno, sauf si vous effectuez une mise à niveau à partir d'une version non Inno.
Deanna

7
Deanna: cela dépend du cas. Pour certains programmes avec des systèmes de plugins automatiques, qui lisent tout ce qui se trouve dans un dossier, la suppression des anciens fichiers est un must absolu lors de l'installation d'une nouvelle version, et le simple fait d'exécuter la désinstallation est généralement le moyen le plus propre de le faire.
Nyerguds

1
@Nyerguds Mais InnoSetup répond à cela en ayant une option pour supprimer certains fichiers / dossiers avant le début de l'installation (indicateur "InstallDelete") afin que vous n'ayez toujours pas besoin de désinstaller l'ancienne version en premier.
NickG

3
@NickG: Encore une fois, cela dépend du cas. Ce serait la situation idéale, oui, et de loin la préférée, mais en réalité, il y a pas mal de situations non idéales. Un tel exemple sont les fichiers dll enregistrés, sur de nombreuses versions cibles possibles.
Nyerguds

Réponses:


27

Vous devriez être en mesure de lire la chaîne de désinstallation à partir du registre, étant donné l'AppId (c'est-à-dire la valeur que vous avez utilisée AppIDdans la [Setup]section-). Il peut être trouvé sous Software\Microsoft\Windows\CurrentVersion\Uninstall\{AppId}\(peut être l'un HKLMou l' autre HKCU, alors mieux vaut vérifier les deux) où {AppId}doit être remplacé par la valeur réelle que vous avez utilisée. Recherchez les valeurs UninstallStringou QuietUninstallStringet utilisez la Execfonction pour l'exécuter à partir de votre InitializeSetup()fonction d'événement.

Mise à jour: suppression de la solution alternative non fonctionnelle utilisant une [Run]entrée -section avec {uninstallexe}- merci à tous les commentateurs qui l'ont signalé!


+1, mais utilisez certainement des scripts pour lire l'ancien nom du programme de désinstallation, car sinon cela ne fonctionnera pas si un chemin différent a été entré par l'utilisateur.
mghie

3
Je ne pense pas que la [Run]solution de section fonctionnera, car elle s'exécute trop tard dans le processus d'installation. À partir du manuel d'Inno Setup: La section [Exécuter] est facultative et spécifie un nombre quelconque de programmes à exécuter après l'installation du programme, mais avant que le programme d'installation affiche la boîte de dialogue finale.
Craig McQueen

Veuillez modifier ce message et supprimer la partie [Exécuter], cela ne fonctionne pas. La deuxième partie fonctionne cependant. Merci :-)
Saulius Žemaitaitis

11
Un avertissement: pour une application 32 bits sur Windows 64 bits, le chemin peut être `Software \ Wow6432Node \ Microsoft \ Windows \ CurrentVersion \ Uninstall \ {AppId}`
Adrian Cox

4
@Adrian: Bien que cela puisse être vrai au niveau physique, je pense que les appels WinAPI utilisés par Inno s'en chargeront déjà - du moins si setup.exe lui-même est un processus 32 bits.
Oliver Giesen

112

J'ai utilisé ce qui suit. Je ne suis pas sûr que ce soit la façon la plus simple de le faire, mais cela fonctionne.

Cette utilisation {#emit SetupSetting("AppId")}repose sur le préprocesseur Inno Setup. Si vous ne l'utilisez pas, copiez-collez votre ID d'application directement.

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

Alternatives

Voir aussi ce billet de blog «Exemple de script d'installation Inno pour la comparaison de versions» qui va encore plus loin, lit le numéro de version de toute version précédemment installée et compare ce numéro de version avec celui du package d'installation actuel.


3
merci d'avoir fait référence à mon article de blog. L'exemple complet de ce message est disponible ici, code.google.com/p/lextudio/source/browse/trunk/trunk/setup/…
Lex Li

La seule chose qui manque ici est la prise en charge d'une entrée de désinstallation sous HKCU au lieu de HKLM.
Oliver Giesen

1
Puis-je suggérer d'ajouter la possibilité de désinstaller si un utilisateur a installé l'application, en particulier si l'utilisateur actuel est un administrateur? ... 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
Excellente solution, fonctionne très bien. Cependant, il ouvre une fenêtre pendant l'installation montrant "Désinstaller [nom du logiciel]". Est-il possible d'empêcher cette fenêtre de s'ouvrir? Parce que l'installation de mon logiciel est si rapide que la fenêtre d'installation se ferme avant la fenêtre de désinstallation et ça a l'air bizarre ...
André Santaló

4
@ AndréSantaló Utiliser / VERYSILENT au lieu de / SILENT
Gautam Jain

7

Si vous «souhaitez simplement supprimer les anciennes icônes» (parce que les vôtres ont été modifiées / mises à jour), vous pouvez utiliser ceci:

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

Ceci est exécuté "au début de l'installation" donc supprime fondamentalement les anciennes icônes, et vos nouvelles seront toujours installées là-bas après que cela soit complètement fait.

Je fais juste cela avec chaque installation "au cas où quelque chose aurait changé" icône sage (tout est réinstallé de toute façon).


Si vous avez mis à jour vos icônes, laissez-les simplement écraser. Il n'est pas nécessaire de les supprimer. Eh bien, si vous souhaitez les supprimer, vous pouvez utiliser cette option. C'est la bonne manière. Quoi qu'il en soit, le gars à qui vous parliez (mlaan, Martijn Laan) est l'auteur d'Inno Setup et je pense qu'il sait de quoi il parle :-)
TLama

1
Ouais, c'est lorsque vous voulez renommer ou déplacer une icône que vous en avez besoin. Par exemple, si la v5 en a un nommé "run" et que la v6 l'a renommé en "run basic" si un utilisateur installe v5 puis v6, ils finiront avec 2 icônes, alors que vous en vouliez vraiment 1 ("run basic"). Donc, cette astuce finit par être nécessaire (@mlaan +1 pour changer le comportement par défaut d'innosetup afin de supprimer les anciennes icônes et de ne pas en avoir besoin ...)
rogerdpack

6

Lorsque vous utilisez Inno Setup, il n'y a aucune raison de désinstaller une version précédente à moins que cette version n'ait été installée par un programme d'installation différent. Sinon, les mises à niveau sont gérées automatiquement.


6
Notre programme a changé de structure, donc l'ancienne version doit être désinstallée.
Quan Mai

11
Non, vous pouvez ajouter des entrées à votre script pour gérer le changement de structure lors d'une mise à jour.
mlaan

@mlaan Et quelles seraient ces entrées?
mythofechelon le

1
Vous pouvez simplement utiliser une [InstallDelete]section pour supprimer les anciens fichiers / répertoires. Les nouveaux fichiers seront ensuite placés aux bons emplacements lors de l'installation.
daiscog

2
Si vous mettez à niveau une bibliothèque tierce comme DevExpress, qui a des numéros de version dans les noms de DLL (comme 15.1 installé avant et 15.2 maintenant), vous souhaitez supprimer l'ancienne version. IMHO c'est une bonne raison.
Thomas Weller

2

La réponse fournie par Craig McQueen est totalement viable. Cependant, j'ajouterais ces commentaires:

  • Le {#emit SetupSetting("AppId")}code ne fonctionne pas pour moi, alors j'ajoute simplement mon identifiant d'application.
  • Je ne voulais pas exécuter mon programme de désinstallation, car j'ai un fichier de configuration INI stocké dans le dossier AppData / qui est supprimé par le programme de désinstallation, et je ne veux pas qu'il soit effacé lors de l'installation d'une nouvelle version. J'ai donc modifié un peu le code fourni par Craig McQueen pour supprimer le répertoire où est installé le programme, après avoir récupéré son chemin.

Donc, concernant le code de Craig McQueen, les changements sont:

  • Récupérez la InstallLocationclé au lieu de la UninstallStringclé.
  • Utilisez la DelTreefonction au lieu de laExec(sUnInstallString, ...)

1

Pour toute personne qui utilise ce qui est GetUninstallString()suggéré ci-dessus pour forcer une désinstallation à l'intérieur CurStepChanged()et a des problèmes de mise en cache du disque, voir ci-dessous une solution connexe qui attend en fait un certain temps après la désinstallation pour que l'exe du programme de désinstallation soit supprimé!

Problème de mise en cache du disque avec inno-setup?


0

Vous pouvez exécuter un programme de désinstallation dans la section [code]. Vous devez comprendre comment obtenir le chemin du programme de désinstallation existant. Pour plus de simplicité lorsque j'installe mes applications, j'ajoute une valeur de chaîne de registre qui pointe vers le dossier contenant le programme de désinstallation, et j'exécute simplement le programme de désinstallation dans le rappel InitializeWizard.

Gardez à l'esprit que les noms des programmes de désinstallation d'Inno sont tous de la forme uninsnnn.exe, vous devez en tenir compte dans votre code.


0

J'ai modifié le code @Crain Mc-Queen, je pense que ce code est meilleur car il n'est pas nécessaire de le modifier dans un projet différent:

[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

J'ai dû louper quelque chose. Les nouveaux fichiers sont copiés dans le répertoire cible avant la suppression de l'ancienne installation. Puis vient le programme de désinstallation les supprime et supprime le répertoire.


2
Je ne suis pas sûr de ce que vous essayez de dire, mais notez qu'il ne s'agit pas toujours de copier des fichiers. Imaginez que vous ayez installé votre produit, qui avec la prochaine version est livré avec une structure de fichiers totalement modifiée, où de nombreux fichiers d'origine ont été supprimés et les nouveaux fichiers ont des noms différents et sont stockés dans différents répertoires. Quelle serait la manière la plus simple de mettre à niveau? Ne serait-ce pas désinstaller la version précédente?
TLama

J'utilise INNO pour installer un pilote et ses applications d'accompagnement. Naturellement, l'installation / la suppression du pilote ne se fait pas directement par INNO. Au contraire, INNO copie une application d'installation / de suppression de pilote puis l'exécute. Désinstallation effectuée de la même manière: INNO exécute le suppresseur de pilote, puis supprime les fichiers.
Shaul

-8

N'utilisez pas la section [Exécuter], mais [UninstallRun]. En fait, les programmes sous [Exécuter] sont exécutés après l'installation, provoquant la désinstallation de votre programme immédiatement après l'installation: - | Au lieu de cela, la section [UninstallRun] est évaluée avant l'installation.


3
[UninstallRun]n'est pas une solution à la question.
Craig McQueen le

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.