Inno Setup: Làm thế nào để tự động gỡ cài đặt phiên bản đã cài đặt trước đó?


88

Tôi đang sử dụng Inno Setup để tạo trình cài đặt.

Tôi muốn trình cài đặt tự động gỡ cài đặt phiên bản đã cài đặt trước đó, thay vì ghi đè nó. Làm thế nào tôi có thể làm điều đó?


2
Lưu ý rằng như mlaan đã nói , thông thường không cần thực hiện điều này với thiết lập dựa trên Inno trừ khi bạn đang nâng cấp từ phiên bản không phải Inno.
Deanna,

7
Deanna: tùy từng trường hợp. Đối với một số chương trình có hệ thống plugin tự động, có thể đọc bất kỳ thứ gì trong thư mục, việc xóa các tệp cũ là điều tuyệt đối bắt buộc khi cài đặt phiên bản mới và chỉ cần chạy gỡ cài đặt thường là cách dễ dàng nhất để thực hiện việc này.
Nyerguds

1
@Nyerguds Nhưng InnoSetup phục vụ cho điều đó bằng cách có tùy chọn xóa các tệp / thư mục nhất định trước khi bắt đầu cài đặt (cờ "InstallDelete"), vì vậy trước tiên bạn vẫn không cần gỡ cài đặt phiên bản cũ.
NickG

3
@NickG: Một lần nữa, tùy trường hợp. Đó sẽ là tình huống lý tưởng, vâng, và cho đến nay là tình huống được ưu tiên, nhưng trên thực tế, có khá nhiều tình huống không lý tưởng. Một ví dụ như vậy là các tệp dll đã đăng ký, trên nhiều phiên bản đích có thể.
Nyerguds

Câu trả lời:


27

Bạn sẽ có thể đọc chuỗi gỡ cài đặt từ sổ đăng ký, với AppId (tức là giá trị bạn đã sử dụng AppIDtrong [Setup]-section). Nó có thể được tìm thấy dưới Software\Microsoft\Windows\CurrentVersion\Uninstall\{AppId}\(có thể là một trong hai HKLMhoặc HKCU, vì vậy tốt nhất hãy kiểm tra cả hai) ở đâu {AppId}nên được thay thế bằng giá trị thực mà bạn đã sử dụng. Tìm kiếm các giá trị UninstallStringhoặc QuietUninstallStringvà sử dụng Exechàm để chạy nó từ InitializeSetup()hàm sự kiện của bạn .

Cập nhật: đã loại bỏ giải pháp thay thế không hoạt động bằng cách sử dụng [Run]mục nhập -section with {uninstallexe}- cảm ơn tất cả những người bình luận đã chỉ ra điều này!


+1, nhưng chắc chắn sử dụng tập lệnh để đọc tên trình gỡ cài đặt cũ, vì nếu không, nó sẽ không hoạt động nếu người dùng đã nhập một đường dẫn khác.
mghie

3
Tôi không nghĩ rằng [Run]giải pháp phần sẽ hoạt động, vì nó chạy quá muộn trong quá trình cài đặt. Từ hướng dẫn sử dụng Inno Setup: Phần [Run] là tùy chọn và chỉ định bất kỳ số lượng chương trình nào sẽ thực thi sau khi chương trình đã được cài đặt thành công, nhưng trước khi chương trình Setup hiển thị hộp thoại cuối cùng.
Craig McQueen

Vui lòng chỉnh sửa bài đăng này và loại bỏ phần [Chạy], nó không hoạt động. Phần thứ hai hoạt động mặc dù. Cảm ơn bạn :-)
Saulius Žemaitaitis

11
Một cảnh báo: cho một ứng dụng 32 bit trên Windows 64 bit, con đường có thể được `Software \ Wow6432Node \ Microsoft \ Windows \ CurrentVersion \ Uninstall \ {AppID}`
Adrian Cox

4
@Adrian: Mặc dù điều đó có thể đúng ở lớp vật lý, nhưng tôi nghĩ rằng các lệnh gọi WinAPI được sử dụng bởi Inno sẽ xử lý điều đó - ít nhất nếu bản thân setup.exe là một quy trình 32 bit.
Oliver Giesen

112

Tôi đã sử dụng những điều sau đây. Tôi không chắc đó là cách đơn giản nhất để làm điều đó nhưng nó hoạt động.

Điều này sử dụng {#emit SetupSetting("AppId")}dựa vào Bộ xử lý Inno Setup. Nếu bạn không sử dụng, hãy cắt và dán ID ứng dụng của bạn vào trực tiếp.

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

Giải pháp thay thế

Xem thêm bài đăng trên blog này "Mẫu tập lệnh thiết lập đổi mới để so sánh phiên bản" tiến thêm một bước và đọc số phiên bản của bất kỳ phiên bản nào đã được cài đặt trước đó và so sánh số phiên bản đó với số phiên bản của gói cài đặt hiện tại.


3
cảm ơn đã tham khảo bài đăng trên blog của tôi. Mẫu đầy đủ cho bài đăng đó có sẵn tại đây, code.google.com/p/lextudio/source/browse/trunk/trunk/setup/…
Lex Li

Điều duy nhất còn thiếu ở đây là hỗ trợ cho mục Gỡ cài đặt theo HKCU thay vì HKLM.
Oliver Giesen

1
Tôi có thể đề xuất thêm khả năng gỡ cài đặt nếu bất kỳ người dùng nào đã cài đặt ứng dụng, đặc biệt nếu người dùng hiện tại là Quản trị viên không? ... 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
Giải pháp tuyệt vời, hoạt động tốt. Tuy nhiên, nó sẽ mở ra một cửa sổ trong khi cài đặt hiển thị "Đang gỡ cài đặt [tên phần mềm]". Có thể ngăn cửa sổ này xuất hiện không? Bởi vì cài đặt phần mềm của tôi là quá nhanh đến nỗi khi cài đặt đóng cửa sổ trước khi cửa sổ gỡ bỏ cài đặt và có vẻ kỳ lạ ...
André Santaló

4
@ AndréSantaló Sử dụng / VERYSILENT thay vì / SILENT
Gautam Jain

7

Nếu bạn "chỉ muốn xóa các biểu tượng cũ" (vì của bạn đã thay đổi / cập nhật), bạn có thể sử dụng điều này:

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

Thao tác này được chạy "khi bắt đầu cài đặt" vì vậy về cơ bản loại bỏ các biểu tượng cũ và các biểu tượng mới của bạn sẽ vẫn được cài đặt ở đó sau khi hoàn tất.

Tôi chỉ làm điều này với mỗi lần cài đặt biểu tượng "trong trường hợp mọi thứ đã thay đổi" khôn ngoan (tất cả đều được cài đặt lại).


Nếu bạn có cập nhật cho các biểu tượng của mình, chỉ cần để chúng ghi đè. Không cần phải loại bỏ chúng. Nếu bạn muốn xóa chúng, bạn có thể sử dụng tùy chọn này. Đó là cách chính xác. Dù sao, anh chàng bạn đang nói chuyện với (mlaan, Martijn Laan) là Inno Setup tác giả và tôi nghĩ anh ấy biết những gì ông nói về :-)
TLama

1
Vâng, đó là khi bạn muốn đổi tên hoặc di chuyển một biểu tượng, bạn cần điều này. Ví dụ: nếu v5 ​​có một cái tên là "run" và v6 đã đổi tên nó thành "chạy cơ bản" nếu người dùng cài đặt v5 rồi v6, họ sẽ kết thúc với 2 biểu tượng, khi bạn thực sự muốn 1 ("chạy cơ bản"). Vì vậy, thủ thuật này kết thúc là cần thiết (@mlaan +1 để thay đổi hành vi mặc định của innosetup là xóa các biểu tượng cũ và không cần điều này ...)
rogerdpack

6

Khi sử dụng Inno Setup, không có lý do gì để gỡ cài đặt phiên bản trước trừ khi phiên bản đó được cài đặt bởi một chương trình cài đặt khác. Nếu không, các nâng cấp sẽ được xử lý tự động.


6
Chương trình của chúng tôi có sự thay đổi về cấu trúc, vì vậy cần gỡ cài đặt phiên bản cũ.
Quán Mai

11
Không, không, bạn có thể thêm các mục vào tập lệnh của mình để xử lý thay đổi cấu trúc trong quá trình cập nhật.
mlaan

@mlaan Và những mục đó sẽ là gì?
mythofechelon

1
Bạn chỉ cần sử dụng một [InstallDelete]phần để xóa các tệp / thư mục cũ. Các tệp mới sau đó sẽ được đặt vào đúng vị trí trong quá trình cài đặt.
daiscog

2
Nếu bạn nâng cấp thư viện của bên thứ ba như DevExpress, có số phiên bản trong tên DLL (như 15.1 được cài đặt trước đây và 15.2 bây giờ) thì bạn muốn xóa phiên bản cũ. IMHO đó là một lý do chính đáng.
Thomas Weller

2

Câu trả lời được cung cấp bởi Craig McQueen là hoàn toàn khả thi. Mặc dù, tôi sẽ thêm những nhận xét đó:

  • Các {#emit SetupSetting("AppId")}mã này không làm việc cho tôi, vì vậy tôi chỉ cần thêm App ID của tôi.
  • Tôi không muốn thực thi chương trình gỡ cài đặt của mình vì tôi có tệp cấu hình INI được lưu trữ trong thư mục AppData / bị trình gỡ cài đặt xóa và tôi không muốn nó bị xóa khi cài đặt phiên bản mới. Vì vậy, tôi đã sửa đổi một chút mã do Craig McQueen cung cấp để xóa thư mục nơi được cài đặt chương trình, sau khi truy xuất đường dẫn của nó.

Vì vậy, liên quan đến mã của Craig McQueen, những thay đổi là:

  • Lấy InstallLocationchìa khóa thay vì UninstallStringchìa khóa.
  • Sử dụng DelTreehàm thay vìExec(sUnInstallString, ...)

1

Đối với bất kỳ ai sử dụng GetUninstallString()đề xuất ở trên để buộc gỡ cài đặt bên trong CurStepChanged()và gặp sự cố bộ nhớ đệm đĩa, hãy xem bên dưới để biết giải pháp liên quan thực sự đợi một lúc sau khi gỡ cài đặt để xóa exe trình gỡ cài đặt!

Sự cố bộ nhớ đệm đĩa với đổi mới thiết lập?


0

Bạn có thể thực thi một trình gỡ cài đặt trong phần [mã]. Bạn phải tìm ra cách lấy đường dẫn đến trình gỡ cài đặt hiện có. Để đơn giản hơn khi cài đặt ứng dụng của mình, tôi thêm giá trị chuỗi đăng ký trỏ đến thư mục chứa trình gỡ cài đặt và chỉ thực thi trình gỡ cài đặt trong lệnh gọi lại InitializeWizard.

Hãy nhớ rằng tên của trình gỡ cài đặt Inno setup đều có dạng uninsnnn.exe, bạn cần phải tính đến điều đó trong mã của mình.


0

tôi đã chỉnh sửa mã @Crain Mc-Queen, tôi nghĩ mã này tốt hơn vì không cần phải sửa đổi trong dự án khác:

[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

Chắc chắn là tôi đang thiếu gì đó. Các tệp mới được sao chép vào thư mục đích trước khi việc xóa cài đặt cũ xảy ra. Sau đó, trình gỡ cài đặt sẽ xóa chúng và xóa thư mục.


2
Tôi không chắc bạn đang muốn nói gì, nhưng lưu ý rằng không phải lúc nào cũng chỉ sao chép tệp. Hãy tưởng tượng rằng bạn đã cài đặt sản phẩm của mình, sản phẩm này với bản phát hành tiếp theo đi kèm với cấu trúc tệp thay đổi hoàn toàn, trong đó nhiều tệp gốc đã bị xóa và các tệp mới có tên khác và được lưu trữ trong các thư mục khác nhau. Cách dễ nhất để nâng cấp là gì? Đó sẽ không phải là gỡ cài đặt phiên bản trước?
TLama

Tôi sử dụng INNO để cài đặt trình điều khiển và các ứng dụng đi kèm. Đương nhiên, việc cài đặt / loại bỏ trình điều khiển thực tế không được thực hiện trực tiếp bởi INNO. Thay vào đó, INNO sao chép ứng dụng trình cài đặt / loại bỏ trình điều khiển sau đó chạy nó. Gỡ cài đặt được thực hiện tương tự: INNO chạy trình gỡ bỏ trình điều khiển, sau đó xóa tệp.
Shaul

-8

Không sử dụng phần [Chạy] mà sử dụng phần [UninstallRun]. Infact, chương trình trong [Run] được thực thi sau khi cài đặt, khiến chương trình của bạn phải gỡ cài đặt ngay sau khi cài đặt: - | Thay vào đó, phần [UninstallRun] được đánh giá trước khi cài đặt.


3
[UninstallRun]không phải là một giải pháp cho câu hỏi.
Craig McQueen

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.