问题
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) :
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) :
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
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:
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;
来源:https://stackoverflow.com/questions/35454876/inno-setup-disable-already-installed-components-on-upgrade