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.