Inno Setup: как автоматически удалить предыдущую установленную версию?

88

Я использую Inno Setup для создания установщика.

Я хочу, чтобы установщик автоматически удалил предыдущую установленную версию, а не перезаписал ее. Как я могу это сделать?

Куан Май
источник
2
Обратите внимание, что, как сказал mlaan, обычно нет необходимости делать это с установкой на основе Inno, если вы не обновляетесь с версии, отличной от Inno.
Deanna
7
Дина: это зависит от случая. Для некоторых программ с автоматическими системами подключаемых модулей, которые читают что-либо в папке, удаление старых файлов является абсолютной необходимостью при установке новой версии, и простой запуск удаления - обычно самый чистый способ сделать это.
Nyerguds 03
1
@Nyerguds Но InnoSetup обслуживает это, имея возможность удалять определенные файлы / папки перед началом установки (флаг «InstallDelete»), поэтому вам все равно не нужно будет сначала удалять старую версию.
NickG
3
@NickG: Опять же, зависит от случая. Это была бы идеальная ситуация, да, и, безусловно, предпочтительная, но на самом деле существует довольно много неидеальных ситуаций. Одним из таких примеров являются зарегистрированные файлы DLL во многих возможных целевых версиях.
Nyerguds

Ответы:

27

Вы должны иметь возможность прочитать строку удаления из реестра, учитывая AppId (т. Е. Значение, которое вы использовали AppIDв разделе [Setup]). Его можно найти в Software\Microsoft\Windows\CurrentVersion\Uninstall\{AppId}\(может быть либо HKLMили HKCU, поэтому лучше проверить оба), где {AppId}следует заменить фактическим значением, которое вы использовали. Найдите значения UninstallStringили QuietUninstallStringи используйте Execфункцию для его запуска из InitializeSetup()функции события.

Обновление: удалено неработающее альтернативное решение с использованием [Run]записи -section с {uninstallexe}- спасибо всем комментаторам, указавшим на это!

Оливер Гизен
источник
+1, но определенно используйте скрипт для чтения старого имени деинсталлятора, потому что в противном случае он не будет работать, если пользователь введет другой путь.
mghie 08
3
Я не думаю, что [Run]решение раздела будет работать, потому что оно запускается слишком поздно в процессе установки. Из руководства по установке Inno: Раздел [Выполнить] является необязательным и указывает любое количество программ, которые необходимо выполнить после успешной установки, но до того, как программа установки отобразит заключительный диалог.
Craig McQueen
Пожалуйста, отредактируйте этот пост и удалите часть [Run], это не работает. Вторая часть работает. Спасибо :-)
Саулюс Жемайтайтис
11
Одно предупреждение: для 32-битного приложения в 64-битной Windows путь может быть `Software \ Wow6432Node \ Microsoft \ Windows \ CurrentVersion \ Uninstall \ {AppId}`
Адриан Кокс,
4
@Adrian: Хотя это вполне может быть правдой на физическом уровне, я думаю, что вызовы WinAPI, используемые Inno, уже позаботятся об этом - по крайней мере, если сам setup.exe является 32-битным процессом.
Оливер Гизен
112

Я использовал следующее. Я не уверен, что это самый простой способ, но он работает.

Это использует, {#emit SetupSetting("AppId")}который полагается на препроцессор Inno Setup. Если вы не используете его, скопируйте и вставьте свой идентификатор приложения напрямую.

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

Альтернативы

См. Также это сообщение в блоге «Пример сценария установки Inno для сравнения версий», в котором идет еще один шаг вперед, где читается номер версии любой ранее установленной версии и сравнивается этот номер с номером текущего установочного пакета.

Крейг МакКуин
источник
3
спасибо за ссылку на мой пост в блоге. Полный образец для этого сообщения доступен здесь, code.google.com/p/lextudio/source/browse/trunk/trunk/setup/…
Лекс Ли,
Единственное, чего здесь не хватает, так это поддержки записи Uninstall в HKCU вместо HKLM.
Оливер Гизен,
1
Могу ли я предложить добавить возможность удаления, если приложение установил какой-либо пользователь, особенно если текущий пользователь является администратором? ... 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
Отличное решение, отлично работает. Однако во время установки открывается окно с сообщением «Удаление [название программы]». Можно ли запретить выскакивание этого окна? Поскольку мое программное обеспечение устанавливается так быстро, что окно установки закрывается перед окном удаления, и это выглядит странно ...
Андре Сантало
4
@ AndréSantaló Используйте / VERYSILENT вместо / SILENT
Gautam Jain
7

Если вы «просто хотите удалить старые значки» (потому что ваши были изменены / обновлены), вы можете использовать это:

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

Это выполняется «в начале установки», поэтому в основном удаляются старые значки, а новые будут установлены там после того, как это будет полностью выполнено.

Я просто делаю это с каждой установкой значка «на случай, если что-то изменилось» (все равно все переустанавливается).

Роджердпак
источник
Если у вас есть обновления для ваших значков, просто позвольте им перезаписать. Их не нужно удалять. Что ж, если вы хотите их удалить, вы можете использовать эту опцию. Это правильный путь. В любом случае, парень, с которым вы разговаривали (млаан, Мартин Лаан), является автором Inno Setup, и я думаю, он знает, о чем говорит :-)
TLama
1
Да, это нужно, когда вы хотите переименовать или переместить значок. Например, если у v5 есть один с именем «run», а v6 переименовал его в «run basic», если пользователь устанавливает v5, а затем v6, они получат 2 значка, тогда как на самом деле вы хотели 1 («run basic»). Таким образом, этот трюк в конечном итоге необходим (@mlaan +1 для изменения поведения по умолчанию innosetup, чтобы удалить старые значки и не нуждаться в этом ...)
rogerdpack 01
6

При использовании Inno Setup нет причин удалять предыдущую версию, если эта версия не была установлена ​​другой программой установки. В противном случае обновления выполняются автоматически.

млаан
источник
6
Структура нашей программы изменилась, поэтому необходимо удалить старую версию.
Quan Mai
11
Нет, это не так, вы можете добавлять записи в свой скрипт для обработки изменения структуры во время обновления.
mlaan
@mlaan А что это были бы за записи?
мифофехелон
1
Вы можете просто использовать [InstallDelete]раздел для удаления старых файлов / каталогов. После этого новые файлы будут размещены в правильных местах во время установки.
daiscog
2
Если вы обновляете стороннюю библиотеку, такую ​​как DevExpress, которая имеет номера версий в именах DLL (например, 15.1, установленная ранее, и 15.2, сейчас), вы захотите удалить старую версию. ИМХО, это веская причина.
Томас Веллер
2

Ответ Крейга Маккуина вполне жизнеспособен. Хотя я бы добавил такие комментарии:

  • {#emit SetupSetting("AppId")}Код не работает для меня, так что я просто добавить свой App ID.
  • Я не хотел запускать свою программу удаления, потому что у меня есть файл конфигурации INI, хранящийся в папке AppData /, которая удаляется программой удаления, и я не хочу, чтобы он стирался при установке новой версии. Итак, я немного изменил код, предоставленный Крейгом Маккуином, чтобы удалить каталог, в котором установлена ​​программа, после получения пути к ней.

Итак, что касается кода Крейга Маккуина, изменения таковы:

  • Получите InstallLocationключ вместо UninstallStringключа.
  • Используйте DelTreeфункцию вместоExec(sUnInstallString, ...)

источник
1

Для тех, кто использует GetUninstallString()предложенное выше для принудительного удаления внутри CurStepChanged()и имеет проблемы с кэшированием диска, см. Ниже соответствующее решение, которое на самом деле ждет некоторое время после удаления, чтобы удалить exe-файл программы удаления!

Проблема с кешированием диска с inno-setup?

фубар
источник
0

Вы можете запустить деинсталлятор в разделе [код]. Вы должны выяснить, как получить путь к существующему деинсталлятору. Для простоты, когда я устанавливаю свои приложения, я добавляю значение строки реестра, которое указывает на папку, содержащую программу удаления, и просто запускаю программу удаления в обратном вызове InitializeWizard.

Имейте в виду, что все имена деинсталляторов установки Inno имеют форму uninsnnn.exe, вы должны учитывать это в своем коде.

Джим в Техасе
источник
0

Я отредактировал код @Crain Mc-Queen, я думаю, что этот код лучше, потому что его не нужно изменять в другом проекте:

[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
источник
-1

Я, должно быть, что-то упускаю. Эти новые файлы копируются в каталог назначения до появления удаления старой установки. Затем деинсталлятор удаляет их и удаляет каталог.

Шауль
источник
2
Я не уверен, что вы пытаетесь сказать, но учтите, что это не всегда просто копирование файлов. Представьте, что вы установили свой продукт, который в следующем выпуске поставляется с полностью измененной файловой структурой, при этом многие исходные файлы были удалены, а новые файлы имеют разные имена и хранятся в разных каталогах. Какой способ обновления был бы самым простым? Разве это не было бы удалением предыдущей версии?
TLama
Я использую INNO для установки драйвера и сопутствующих приложений. Естественно, фактическая установка / удаление драйвера не выполняется непосредственно компанией INNO. Вместо этого INNO копирует приложение для установки / удаления драйверов, а затем запускает его. Удаление производится аналогично: INNO запускает программу удаления драйверов, затем удаляет файлы.
Шауль
-8

Не используйте раздел [Run], а [UninstallRun]. Фактически, программы из раздела [Выполнить] выполняются после установки, что приводит к удалению вашей программы сразу после установки: - | Вместо этого перед установкой проверяется раздел [UninstallRun] .

Андреа Феррони псевдоним Bubbakk
источник
3
[UninstallRun]это не решение вопроса.
Craig McQueen
-8

Перейдите по этой ссылке: http://news.jrsoftware.org/news/innosetup/msg55323.html

В функции InitializeSetup () вы можете вызвать "MSIEXEC / x {идентификатор вашей программы}" после того, как пользователь предложит удалить старую версию.

Тонни Нгуен
источник
5
MSIEXEC работает только с пакетом MSI. Это не относится к Inno Setup.
Лекс Ли