2
votes

We have actually a setup who has many components choice during install and we want to disable component already installed (or make it grey / fixed) when customer reinstall our software.

For example during first install we have 3 component like this:

Component
* International
* French
* German

During first install, all components can be selected.

Recognize that chooses the "German" pack, when user make a reinstall of product (with the same installer) to get a new language pack, we want to have something like that:

Component
* International
* French
* German (already installed)

Where "German" can't be selected...

Is there a way to do that with Inno Setup?

I found InnoSetup: Disable components page on upgrade, but the example disables whole component page, and I want to keep it.

EDIT :

That's working for 2 install. But if I do a third install (for installing the last component), the setup take the component of second install, but not the first.

I think this is because the setup overwrites the registry key of the first installation and therefore does not see the components installed in the early ...

Here is the log and registry key on first install (english selected) :

after first install

2016-03-22 13:57:56.913   New install de Bim

Here is the log and registry key on second install (english is grey and can't be selected, french is selected) :

after second install

Created temporary directory: C:\Users\mea\AppData\Local\Temp\is-QV8N6.tmp
2016-03-22 14:00:54.354   Upgrading, previously installed components are [languagepacks,languagepacks\english,canecorevit,canecorevit\2016]
2016-03-22 14:00:54.354   Found installed component [languagepacks]
2016-03-22 14:00:54.354   Disabling installed component [languagepacks] as [Content] at 0
2016-03-22 14:00:54.355   Found installed component [languagepacks\english]
2016-03-22 14:00:54.355   Disabling installed component [languagepacks\english] as [Pack International] at 1
2016-03-22 14:00:54.355   Found installed component [canecorevit]
2016-03-22 14:00:54.356   Found installed component [canecorevit\2016]
2016-03-22 14:00:54.356   Disabling installed component [canecorevit\2016] as [REVIT 2016] at 5
2016-03-22 14:02:48.691   Message box (Yes/No):
                          L'assistant d'installation a détecté que les composants suivants sont déjà installés sur votre système :

                          Pack International

                          Désélectionner ces composants ne les désinstallera pas pour autant.

                          Voulez-vous continuer malgré tout ?
2016-03-22 14:02:49.808   User chose Yes.
2016-03-22 14:02:56.000   Starting the installation process.

And here is the log and registry key on third install

enter image description here

Created temporary directory: C:\Users\mea\AppData\Local\Temp\is-J7G5A.tmp
2016-03-22 14:07:41.582   Upgrading, previously installed components are [languagepacks,languagepacks\french,canecorevit,canecorevit\2016]
2016-03-22 14:07:41.583   Found installed component [languagepacks]
2016-03-22 14:07:41.583   Disabling installed component [languagepacks] as [Content] at 0
2016-03-22 14:07:41.583   Found installed component [languagepacks\french]
2016-03-22 14:07:41.584   Disabling installed component [languagepacks\french] as [Pack France] at 2
2016-03-22 14:07:41.584   Found installed component [canecorevit]
2016-03-22 14:07:41.584   Found installed component [canecorevit\2016]
2016-03-22 14:07:41.585   Disabling installed component [canecorevit\2016] as [REVIT 2016] at 5
2016-03-22 14:08:14.122   Message box (Yes/No):
                          L'assistant d'installation a détecté que les composants suivants sont déjà installés sur votre système :

                          Pack France

                          Désélectionner ces composants ne les désinstallera pas pour autant.

                          Voulez-vous continuer malgré tout ?
2016-03-22 14:08:15.132   User chose Yes.

So apparently, inno-setup write after each install on Software\Microsoft\Windows\CurrentVersion\Uninstall\AppId_is1\Inno Setup: Selected Components and that's why it only work for the second install, but doesn't remember that component are choose in first install during a third one.

1
Typo: Deutch means Deutsch (German) or? ;)Smartis
@Smartis Yes that's right. Wrong typo from me.Algorys

1 Answers

1
votes

You can extract list of installed components from registry value

Software\Microsoft\Windows\CurrentVersion\Uninstall\AppId_is1\Inno Setup: Selected Components

The problem is that the list uses component names and there's no way to map the names to the checkboxes as only descriptions are exposed programmatically (see also How to allow to only install specific components in InnoSetup?

Luckily the previously installed components will be checked by Inno Setup itself. So the easiest solution is to disable all initially checked components. This will break though if you add a new component in the upgrade that is checked by default.

Another way is to use WizardSelectedComponents support function that can return both list of selected component names and descriptions. So it can be used to map descriptions to names and back. But only for selected components. Though that should be enough for your specific purpose. See SelectedComponentDescriptionToName in my code.

A limitation is that the description must be unique, otherwise the mapping fails. So you cannot for example have multiple "Deutsch" subcomponents of different parent components (that would require more complicated code and won't work at all if the parent component has checkablealone flag)

#define AppId "myapp"
#define InnoSetupReg \
  "Software\Microsoft\Windows\CurrentVersion\Uninstall\" + AppId + "_is1"
#define InnoSetupSelectedComponentsReg "Inno Setup: Selected Components"

[Setup]
AppId={#AppId}
...

[Code]

function ExtractToken(var S: string): string;
var
  P: Integer;
begin
  P := Pos(',', S);
  if P > 0 then
  begin
    Result := Copy(S, 1, P - 1);
    Delete(S, 1, P);
  end
    else
  begin
    Result := S;
    S := '';
  end;
end;

function SelectedComponentDescriptionToName(Description: string): string;
var
  Descriptions: string;
  Names: string;
begin
  Descriptions := WizardSelectedComponents(True);
  Names := WizardSelectedComponents(False);

  while Descriptions <> '' do
  begin
    Result := ExtractToken(Names);
    if RemoveQuotes(ExtractToken(Descriptions)) = Description then
    begin
      Exit;
    end;
  end;

  Result := '';
end;

procedure InitializeWizard();
var
  Upgrade: Boolean;
  SelectedComponents: string;
  Component: string;
  Name: string;
  I: Integer;
begin
  Upgrade :=
    RegQueryStringValue(HKCU, ExpandConstant('{#InnoSetupReg}'), 
      '{#InnoSetupSelectedComponentsReg}', SelectedComponents) or
    RegQueryStringValue(HKLM, ExpandConstant('{#InnoSetupReg}'),
      '{#InnoSetupSelectedComponentsReg}', SelectedComponents);

  if not Upgrade then
  begin
    Log('New install');
  end
    else
  begin
    Log(Format('Upgrading, previously installed components are [%s]', [
      SelectedComponents]));

    while SelectedComponents <> '' do
    begin
      Component := ExtractToken(SelectedComponents);

      Log(Format('Found installed component [%s]', [Component]));

      for I := 0 to WizardForm.ComponentsList.Items.Count - 1 do
      begin
        if WizardForm.ComponentsList.State[I] = cbChecked then
        begin
          Name :=
            SelectedComponentDescriptionToName(
              WizardForm.ComponentsList.ItemCaption[I]);

          if Name = Component then
          begin
            Log(Format('Disabling installed component [%s] as [%s] at %d', [
              Name, WizardForm.ComponentsList.ItemCaption[I], I]));
            WizardForm.ComponentsList.ItemEnabled[I] := False;
          end;
        end;
      end;
    end;
  end;
end;