Overriding feature on command line when installing over the top of previous install - wix

I have an installer and I have a feature that I can turn on or off if I pass on the msiexec.exe commandline a property named FEATUREX to be 0 or 1. If the value is zero, the feature is not installed. If the value is 1, then the feature is installed. It's a COM component. Part of the install always installs the COM component so that it can be enabled later by changing some registry values in the system. The part of installing the COM component is not FEATUREX since the COM component is always installed. Enabling the COM component to the system is FEATUREX and it adds a CLSID to a subkey in Windows and the system starts using the COM component.
If the software is not installed or has been uninstalled, then my logic works. The problem occurs when I want to install over the top of a previous installation. I do not want to require that the previous installation be uninstalled. I just want to install over the top, and if a sysadmin installs over the top and enables or disables FEATUREX, then FEATUREX should be installed or not installed depending on the value of FEATUREX on the command line.
Usually customers might want to install with FEATUREX disabled, then enable FEATUREX (independent of the installer), but setting the registry key.
However, when installing over the top, the system ignores FEATUREX property on the command line and the real Feature X is enabled or disabled depending on whether it was originally enabled or disabled.
I tried changing the GUID of the Component, and that didn't work...IDK...I was just guessing.
If I change the Feature ID from ActivateFeatureX to ActivateFeatureX2 then the command line property FEATUREX works...but then, if the property is not passed on the command line, Feature X is not installed if it was previously installed.
I was wondering what the solution might be? I have thoughts of a simple checkbox tied to a registry DWORD value such that the COM component would fail to be created if the DWORD value is 0... but IDK.
<Feature Id='ActivateFeatureX' Title='Feature X' Description='If this dropdown is changed from the red "X" to the grey disk icon, then Feature X will be enabled'
Level='2' AllowAdvertise='no' InstallDefault="local" Display="3">
<Condition Level="1">FEATUREX=1</Condition>
<Condition Level="0">FEATUREX=0</Condition>
<ComponentRef Id='ActivateFeatureX' />
</Feature>
<Component Id="ActivateFeatureX" Location="local" Guid="4c0d547f-d9ae-4c5f-bd21-d8360c00dd46">
<RegistryValue Root='HKLM' Key='SOFTWARE\Microsoft\Windows\CurrentVersion\SOMEWHERE\SOMEHOW\{$(var.FeatureXRegistryGUID)}' Type='string' Value='$(var.TheProject.TargetName)' KeyPath='yes' />
</Component>

Instead of trying to use a Condition to control the state of ActivateFeatureX (which has limitations during repair and upgrade), consider using the ADDLOCAL and REMOVE properties to change the state of the Feature directly.

Related

How to restrict user to change feature in case modify and upgrade in installer?

I have a installer which asks the user to select feature. Whatever user selects, it will never be changed in case of modify and upgrade the installation. For example:
There are three features in my installer which are below:
<Feature Id="Standalone" Title="Standalone" Level="2">
</Feature>
<Feature Id="CentralCase" Title="Central case" Level="2" >
</Feature>
<Feature Id="MiddleEF" Title="Middle Ef" Level="2" Display="expand">
<Feature Id="GUI" Title="Client" Level="3"></Feature>
<Feature Id="AppServer" Title="Application Server" Level="3">
</Feature>
</Feature>
Now suppose user starts the installation and select the first feature which is standalone and install it. Now if user wants to modify, he should not allowed to change feature or even if user wants to upgrade, user should also not allowed to change feature. He can only upgrade what he selected at first time. Is there any way to do this?
ARPNOMODIFY: I guess it depends how critical it is that these features never change. You can set the ARPNOMODIFY in the MSI to
1 and there will be no button to invoke Modify from:
<Property Id="ARPNOMODIFY" Value="1" Secure="yes" />
Disclaimer below. Here be dragons.
msiexec.exe: However, you can still invoke modify by launching the MSI file itself (the default dialog sets should correctly disable the modify button though), but worse: you can go via the msiexec.exe command line and change anything you want:
msiexec /i "MySetup.msi" ADDLOCAL=MyFeature
This might be OK since it would appear to be seldomly used. However, you should be aware that remote management systems often rely on the msiexec.exe command line to handle MSI deployment, and as such the deployment system could be used to change feature state easily (via the deployment tool GUI, no command lines to deal with).
Custom Action: I don't know of an auto-magic way to abort setup if the user tries to modify the feature structure invoked via the msiexec.exe command line, but I suppose you can use a custom action maybe right before InstallInitialize in the InstallExecuteSequence to abort the installation if ADDLOCAL, REMOVE or ADVERTISE are set? If you do not condition this custom action properly, it could cause a package that won't uninstall at all or upgrade properly.
Some unverified conditioning suggestions: How to execute conditional custom action on install and modify only?
MigrateFeatureStates: For a major upgrade the GUI will not run as if it is running modify, but a fresh installation (since the product GUID is new). Hence the original installation GUI is shown and not the modify one. Accordingly you might need to disable some GUI controls or hide whole dialogs to prevent feature selection (not sure in WiX default dialogs). Added a link for that below. The standard action MigrateFeatureStates will take care of preserving the feature installation states between versions, provided you haven't done anything drastic to the feature structure. You enable this standard action to run in the Upgrade table. Should be default to run in WiX MSIs I think.
UPDATE:
Preselected Property: There is a special property called Preselected that is to automatically hide feature selection. You can try to set it or check whether it is set automatically by WiX to see if it hides feature selection. I have honestly never tried it.
Some Further Resources:
Hiding whole dialogs: Wix, custom dialog when previous version exists

Skip MSI if already installed

I have a WIX installer in which I install my program and .NET Core 1.0.5. The .NET Core is installed in silent mode:
<ExePackage InstallCommand="/q" Id = "DotNetCore.Setup" SourceFile="..\DotNetCore\DotNetCore.exe" />
On a clean system, the installer just goes fine. If I try to reinstall it, I get a reboot. Probably the .NET installer detected to be already installed and triggered the repair feature on its own. Is there any way to skip the .NET Core installation if it's already installed?
I tried looking for command line parameters but nothing seems useful
The ExePackage element has the DetectCondition property. This means that you can specify a condition such that, if the condition evaluates to false, the package will be installed. You can combine this with an util:RegistrySearch element which can be used to search through the registry to detect if the .NET Core has already been installed.
In order to perform the registry search, you will first need to find a registry key which is present whenever .NET is installed.
Find the "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\",
(or "HKLM\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\" on 64-bit machines) key in your registry, then locate the subkey which corresponds to the .NET Core - this key should have a value for "DisplayName" which should be ".NET Core" or something similar.
The correct key, once found, should have a name which is a string of hex characters - this is one of the GUIDs corresponding to the .NET Core program. You can then use the following code to allow the installer to search for the presence of this key:
<util:RegistrySearch Id="VCRedistTest32" Root="HKLM" Key="SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{YOUR_GUID_HERE}" Result="exists" Variable="DOTNETPresent" Win64="no"/>
(Use Win64="yes" instead for the 64-bit registry)
You can then use the following for the ExePackage:
<ExePackage InstallCommand="/q" Id = "DotNetCore.Setup" SourceFile="..\DotNetCore\DotNetCore.exe" DetectCondition="DOTNETPresent"/>
Don't forget to add the reference to the util extension to the top-level wix element:
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
You need to use DetectCondition attribute and registry search that assign property in case the .net Core is installed.
ExePackage Element - DetectCondition
"A condition that determines if the package is present on the target system. This condition can use built-in variables and variables returned by searches. This condition is necessary because Windows doesn't provide a method to detect the presence of an ExePackage. Burn uses this condition to determine how to treat this package during a bundle action; for example, if this condition is false or omitted and the bundle is being installed, Burn will install this package."

Check condition and continue installation using Wix Toolset

I have a .wxs file for program installation. I want to pass a part of installation depending on the OS version (for ex. execute if OS verion > XP and pass if <= XP) and continue to install other parts. It means that i've already installed some components, and during the installation want to check (according to the current OS), should i install next component or not. If yes - install, if not - installation of the component should be skipped. Of course, after that full installation should continue. I don't want to abort the installation and remove installed components, just skip one component. How can i do it?
Also, I found this one link. There is a block install case, but i want to continue installation after condition.
There is another assumption - we can use
<?if *condition* ?> ... <?endif?>
blocks, but i really don't understand how to get OS info there.
Any ideas?
You don't want to use <?if ... ?> these are preprocessor directives like #ifdef and #ifndef in c++ and just determine whether or not the part between the if is included for compilation or not. This will only be evaluated at built time on the build machine.
Something like this would stop your installation from running on Windows XP or earlier machines.
<Condition Message="!(loc.OSNotSupported)">Installed OR VersionNT > 501 OR VersionNT64 > 501</Condition>
Similar conditions can be used to dictate whether or not a component is installed, a feature is installed, or a custom action is run.
If you update your question and leave a comment on this answer I will try to update my answer to better answer you question.
To install certain components dependent on the OS of the machine you are running is quite simple, just add the following to your <component>
<Component Id="InstallMeOnXP" Guid="*">
<Condition>VersionNT <= 501 OR VersionNT64 <= 501</Condition>
<File Id="XPOnly.dll" KeyPath="yes" Source="$(var.BinariesDir)\_bin\XPOnly.dll" />
</Component>
If the condition evaluates to TRUE then the component will be installed, otherwise it will not be installed. More info on the Condition element in wix.
Here is the information about the default VersionNT and VersionNT64 properties for the Windows Installer.

Install optional desktop shortcut for all users

I want to make a wix installer, with an optional desktop shortcut for all users. Optional means, I cannot put it in the component of the main exe, but in a separate component, so that I can set a separate feature for the shortcut.
When doing this, wix complains:
error LGHT0204 : ICE38: Component DesktopShortcut installs to user profile. It must use a registry key under HKCU as its KeyPath, not a file.
So I set an aditional registry key like this:
<Component Id="DesktopShortcut" Guid="767f3e19-9a7d-4793-9782-75516494a57c">
<Shortcut Target="[APPLICATIONROOTDIRECTORY]ocean.exe" Id="OceanShortcutDesktop" Name="$(var.ProductName)" WorkingDirectory='APPLICATIONROOTDIRECTORY' Icon="OceanIcon"/>
<RegistryValue Root='HKCU' Key='Software\[Manufacturer]\[ProductName]\installs' Name="desktopshortcut" Type='string' Value='' KeyPath='yes' />
</Component>
The shortcut goes in "All Users" since I set the ALLUSERS=1 property. However, there is no equivalent for registry, and the key goes in the current (installing) user registry. This is IMHO an issue : if the user profile gets deleted, the shortcut will not be deleted anymore on uninstall.
Using "HKLM" instead causes an error in wix (mixing per-user and per-machine):
error LGHT0204 : ICE38: Component DesktopShortcut installs to user profile. It's KeyPath registry key must fall under HKCU.
This is quite stupid from wix, as "All Users" component should be linked to the machine, and not to a normal user, as there is no "all users" registry.
Any idea?
You can try to change the registry root to 'HKMU' (From what I remember it's supposed to to write to the HKEY_LOCAL_MACHINE when the install is per-machine and under HKEY_CURRENT_USER when its not).
If you change the root to 'HKMU' you will get the ICE57 error (Component 'DesktopShortcut' has both per-user data and a keypath that can be either per-user or per-machine.)
You can suppress this error and the installer will create all-users shortcut
(It worked when I tried it, but I'm not sure it's the best way to do this).
(To disable the ICE57 error see: How to fix ICE57.Per-User installation)

How to fix ICE57.Per-User installation

Our application writes some settings to the registry into the HKCU hive during runtime. I want to delete this settings during uninstall. Here is code:
<Fragment>
<DirectoryRef Id="INSTALLLOCATION" DiskId="1" FileSource="$(var.SourceDirProject)\">
<Component Id="DeleteHkcuManufacturerHive" Guid="GUID">
<Condition>REMOVE="ALL" AND NOT UPGRADINGPRODUCTCODE</Condition>
<CreateFolder/>
<RemoveRegistryKey Action="removeOnUninstall"
Id="HKCUkey" Root="HKCU" Key="Software\$(var.Manufacturer)"/>
</Component>
</DirectoryRef>
</Fragment>
ICE57: Component 'DeleteHkcuManufacturerHive' has both per-user and per-machine data with a per-machine KeyPath.
Why I'm getting ICE57? Installation is per-User. Thank's in advance.
UPD: Where is here the per-machine element? May be it is an INSTALLLOCATION=Program Files\ManufacturerDirectory?
You are operating on the HKCU hive which is only available to the current user.
MSDN states:
ICE57 validates that individual components do not mix per-machine and
per-user data. This ICE custom action checks registry entries, files,
directory key paths, and non-advertised shortcuts. Mixing per-user and
per-machine data in the same component could result in only partial
installation of the component for some users in a multi-user
environment.
The ICEs are validations on your installation package. As stated above, ICE57 is to make sure you don't mix up per-machine and per-user constructs. If you must remove entries to HKCU during uninstall (and the software is installed per-machine) then you can turn off that specific validation in Visual Studio in Properties > Tool Settings as shown in the screenshot below:
However, you may want to think about the root cause of your issue. If you are doing a per-machine install, your installer or application should probably not be writing to HKCU as it is only available to the current user, whereas your app is installed for all users.
I've got an answer on wix-users mailing list. Here is Peter Shirtcliffe's answer:
ProgramFiles is a per-machine location. You can only access it when elevated. If you want to install program code in a per-user installation, you should install to %LocalAppData%\Programs.
Remove the condition entirely. The component will be installed but will have no effect until you uninstall the application. At that point, when the component is removed, the registrykey will be removed also.