Inno Setup Uninstall some components only

2019-04-17 04:34发布

问题:

I have a common application -like a media player- for each different retail product that is installed in the same folder i.e.

C:\program files\myapp

and the different content -like movies- are all installed in the same folder

C:\program files\myapp\movies

For each separate installation (separate retail product), I just create a shortcut to open the application with the specific content like mediaplayer -f movie1.mp4, but it can open all other content from the same environment.

The problem in my case, is the uninstallation. Inno Setup does not support component selection to uninstall.

My strategy (in my mind at the time) is to present the user a custom form to select the content to uninstall. For example.

[X] Main Application 
[X] Movie1 Full Title Description 
[X] Movie2 Full Title Description
[X] Movie3 Full Title Description

With that way the common uninstall will work for every separate retail installation.

The problems

  1. How to create such a form
  2. How to scan the common shared folder i.e. movies for this example to find all installed content i.e. movie1.mp4, movie2.mp4, .....
  3. How to open and read the header of each movie to read some data like the full title to show it in the uninstallation procedure instead of the filename.

I used to write Pascal code at the DOS era but I now I am a C++/MFC programmer.

Any ideas, recommendations, strategies?

Best regards.

回答1:

Inno Setup does not support partial uninstallations.

Once the uninstallation completes, your whole application with all installed components (movies) will be deleted.

What you can do is to present a custom form with movies list in the InitializeUninstall. If the user chooses to uninstall only (some) movies, but not whole application (the viewer), you delete the movies with your own code and abort the uninstallation (by returning False from the InitializeUninstall). If the user chooses to uninstall everything, you let the uninstallation complete.

function InitializeUninstall(): Boolean;
var
  Form: TSetupForm;
  OKButton, CancelButton: TNewButton;
  CheckListBox: TNewCheckListBox;
  I, Count, Deleted: Integer;
  FindRec: TFindRec;
  MoviePath: string;
  Movies: TArrayOfString;
begin
  MoviePath := ExpandConstant('{app}\movies\');
  Count := 0;
  if FindFirst(MoviePath + '*', FindRec) then
  begin
    try
      repeat
        if FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY = 0 then
        begin
          Inc(Count);
          SetArrayLength(Movies, Count);
          Movies[Count - 1] := FindRec.Name;
        end;
      until not FindNext(FindRec);
    finally
      FindClose(FindRec);
    end;
  end;

  if Count = 0 then
  begin
    Log('Found no movies proceeding with a complete uninstallation');
    Result := True;
  end
    else 
  begin
    Log(Format('Found %d movies', [Count]));

    Form := CreateCustomForm();
    try
      Form.ClientWidth := ScaleX(350);
      Form.ClientHeight := ScaleY(250);
      Form.Caption := 'Uninstall';
      Form.Position := poDesktopCenter;

      CheckListBox := TNewCheckListBox.Create(Form);
      CheckListBox.Parent := Form;
      CheckListBox.Left := ScaleX(10);
      CheckListBox.Width := Form.ClientWidth - 2*CheckListBox.Left;
      CheckListBox.Top := ScaleY(10);
      CheckListBox.Height := Form.ClientHeight - ScaleY(23 + 10 + 10 + CheckListBox.Top);

      CheckListBox.AddCheckBox(
        'Uninstall viewer and all movies', '', 0, True, True, True, True, nil);
      for I := 0 to Count - 1 do
      begin
        CheckListBox.AddCheckBox(Movies[I], '', 1, True, True, False, True, nil);
      end;

      OKButton := TNewButton.Create(Form);
      OKButton.Parent := Form;
      OKButton.Width := ScaleX(75);
      OKButton.Height := ScaleY(23);
      OKButton.Left := Form.ClientWidth - ScaleX(75 + 6 + 75 + 10);
      OKButton.Top := Form.ClientHeight - ScaleY(23 + 10);
      OKButton.Caption := 'OK';
      OKButton.ModalResult := mrOk;
      OKButton.Default := True;

      CancelButton := TNewButton.Create(Form);
      CancelButton.Parent := Form;
      CancelButton.Width := OKButton.Width;
      CancelButton.Height := OKButton.Height;
      CancelButton.Left := OKButton.Left + OKButton.Width + ScaleX(6);
      CancelButton.Top := OKButton.Top;
      CancelButton.Caption := 'Cancel';
      CancelButton.ModalResult := mrCancel;
      CancelButton.Cancel := True;

      Form.ActiveControl := CheckListBox;

      if Form.ShowModal() <> mrOk then
      begin
        Log('User cancelled the uninstallation');
        Result := False;
      end
        else
      begin
        if CheckListBox.State[0] = cbChecked then 
        begin
          Log('User selected complete uninstallation');
          Result := True; 
        end
          else
        begin
          for I := 0 to Count - 1 do
          begin
            if CheckListBox.Checked[I + 1] then
            begin
              if DeleteFile(MoviePath + Movies[I]) then
              begin
                Inc(Deleted);
                Log(Format('Deleted movie %s', [Movies[I]]));
              end
                else
              begin
                MsgBox(Format('Error deleting %s', [Movies[I]]), mbError, MB_OK);
              end;
            end;
          end;

          MsgBox(Format('Deleted %d movies', [Deleted]), mbInformation, MB_OK);
          Result := False; 
        end;
      end;
    finally
      Form.Free();
    end;
  end;
end;