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


88

Я використовую Inno Setup для створення інсталятора.

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


2
Зверніть увагу, що, як сказав mlaan, зазвичай немає потреби робити це з установкою на базі Inno, якщо ви не переходите з іншої версії.
— Дінна

7
Дінна: це залежить від випадку. Для деяких програм з автоматичними системами плагінів, які читають що-небудь у папці, видалення старих файлів є абсолютно необхідним при встановленні нової версії, і просто запуск видалення, як правило, найчистіший спосіб зробити це.
— Nyerguds,

1
@Nyerguds Але InnoSetup відповідає цьому, маючи можливість видалити певні файли / папки перед початком інсталяції (прапор "InstallDelete"), тому вам все одно не потрібно буде спочатку видаляти стару версію.
— NickG

3
@NickG: Знову ж таки, це залежить від випадку. Так було б ідеальною ситуацією, і, безумовно, кращою, але насправді ситуацій, що не є ідеальними, досить багато. Одним з таких прикладів є зареєстровані файли DLL у багатьох можливих цільових версіях.
— Nyerguds

Відповіді:


27

Ви зможете прочитати рядок видалення з реєстру, враховуючи AppId (тобто значення, яке ви використовували AppIDв розділі [Setup]-section). Його можна знайти в розділі Software\Microsoft\Windows\CurrentVersion\Uninstall\{AppId}\(може бути або HKLMабоHKCU , тому краще перевірити обидва), де {AppId}слід замінити фактичним значенням, яке ви використовували. Шукайте значення UninstallStringабо QuietUninstallStringі використовуйте Execфункцію для запуску з вашогоInitializeSetup() функції події.

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


+1, але однозначно використовуйте сценарії для читання старого імені видалення, оскільки в іншому випадку це не спрацює, якщо користувач ввів інший шлях.
— mghie

3
Я не думаю, що [Run]вирішення розділу буде працювати, оскільки воно запізнюється в процесі встановлення. З посібника з налаштування Inno: Розділ [Запуск] є необов’язковим і вказує будь-яку кількість програм, які слід виконати після успішного встановлення програми, але до того, як програма встановлення відобразить остаточне діалогове вікно.
— Крейг МакКвін

Будь ласка, відредагуйте цю публікацію та видаліть частину [Run], вона не працює. Друга частина працює. Дякую :-)
— Saulius Žemaitaitis

11
Одне попередження: для 32-розрядної програми в 64-розрядної Windows шлях може бути `Software \ Wow6432Node \ Microsoft \ Windows \ CurrentVersion \ Uninstall \ {AppId}`
— Адріан Кокс

4
@Adrian: Хоча це може бути правдою на фізичному рівні, я думаю, що виклики WinAPI, використовувані Inno, вже подбають про це - принаймні, якщо сам setup.exe є 32-бітним процесом.
— Олівер Гізен,

112

Я використав наступне. Я не впевнений, що це найпростіший спосіб це зробити, але він працює.

Це використовує те, {#emit SetupSetting("AppId")}що покладається на Inno Setup Preprocessor. Якщо ви не використовуєте це, виріжте і вставте свій ідентифікатор програми безпосередньо.

[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 Setup для порівняння версій", яка йде на крок далі, читає номер версії будь-якої раніше встановленої версії та порівнює номер версії з номером поточного інсталяційного пакету.


3
дякую за посилання на мій допис у блозі. Повний зразок цієї публікації доступний тут, code.google.com/p/lextudio/source/browse/trunk/trunk/setup/…
— Лекс Лі

Єдине, чого тут не вистачає, - це підтримка запису про видалення під 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;
— Терранс,

2
Чудове рішення, чудово працює. Однак під час інсталяції відкриється вікно із повідомленням "Видалення [ім'я програмного забезпечення]". Чи можна запобігти появі цього вікна? Оскільки встановлення мого програмного забезпечення відбувається настільки швидко, що вікно встановлення закривається перед вікном видалення, і це виглядає дивно ...
— Андре Сантало,

4
@ AndréSantaló Використовувати / VERYSILENT замість / SILENT
— Gautam Jain

7

Якщо ви "просто хочете видалити старі значки" (оскільки ваші змінили / оновили), ви можете скористатися цим:

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

Це запускається "на початку інсталяції", таким чином, в основному видаляються старі значки, а ваші нові все одно будуть встановлені там після того, як це буде повністю зроблено.

Я просто роблю це при кожному встановленні піктограми "на випадок, якщо щось змінилося" (все це все одно переінсталюється).


Якщо у вас є оновлення для ваших піктограм, просто дозвольте їм перезаписати. Не потрібно їх видаляти. Ну, якщо ви хочете їх видалити, ви можете скористатися цією опцією. Це правильний спосіб. У будь-якому випадку, хлопець, з яким ви розмовляли (mlaan, Martijn Laan), є автором програми Inno Setup, і я думаю, що він знає, про що йде мова :-)
— TLama

1
Так, саме тоді, коли ви хочете перейменувати або перемістити піктограму, вам це потрібно. Наприклад, якщо у v5 є одна з назвою "запустити", а v6 перейменовує її на "запустити базову", якщо користувач встановить v5, то v6, вони отримають 2 піктограми, коли ви дійсно хотіли 1 ("запустити основну"). Тож цей фокус виявляється необхідним (@mlaan +1 для зміни поведінки за замовчуванням innosetup на видалення старих піктограм і не потрібне це ...)
— rogerdpack

6

Під час використання Inno Setup немає причин видаляти попередню версію, якщо ця версія не була встановлена ​​іншою програмою встановлення. В іншому випадку оновлення обробляються автоматично.


6
Наша програма змінила структуру, тому стару версію потрібно видалити.
— Quan Mai

11
Ні, це не так, ви можете додавати записи до свого сценарію для обробки змін структури під час оновлення.
— mlaan

@mlaan А які це будуть записи?
— mythofechelon 02

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!

Проблема кешування диска з інноваційною установкою?


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;

-1

Напевно, мені чогось не вистачає. Ці нові файли копіюються в каталог призначення до появи видалення старої установки. Потім програма видалення видаляє їх і видаляє каталог.


2
Я не впевнений, що ви намагаєтесь сказати, але зауважте, що не завжди йдеться лише про копіювання файлів. Уявіть, що ви встановили ваш продукт, який у наступному випуску має повністю змінену структуру файлів, де багато оригінальних файлів було видалено, а нові файли мають різні імена та зберігаються в різних каталогах. Яким буде найпростіший спосіб оновлення? Це не буде видалення попередньої версії?
— TLama

Я використовую INNO для встановлення драйвера та супровідних програм. Природно, що фактична інсталяція / видалення драйвера не здійснюється безпосередньо INNO. Навпаки, INNO копіює програму встановлення / видалення драйверів, а потім запускає її. Видалення виконано аналогічним чином: INNO запускає програму для видалення драйверів, а потім видаляє файли.
— Шаул,

-8

Не використовуйте розділ [Run], а [UninstallRun]. Фактично, програма під [Запуск] виконується після встановлення, що призводить до видалення вашої програми відразу після встановлення: - | Натомість перед встановленням перевіряється розділ [UninstallRun] .


3
[UninstallRun]не є рішенням питання.
— Крейг Маккуїн,

-8

Перейдіть за цим посиланням: http://news.jrsoftware.org/news/innosetup/msg55323.html

У функції InitializeSetup () ви можете викликати "MSIEXEC / x {ваш ідентифікатор програми}" після запиту користувача, щоб видалити стару стару версію


5
MSIEXEC працює лише для пакету MSI. Це не стосується програми Inno Setup.
— Лекс Лі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.