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

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

Related

Wix: Reschedule RegisrySearch and set property in Merge Module

I have a merge module which searches for some registry locations to read values and save to Properties. Here is the code segment:
<Fragment Id="RegSearch">
<Property Id="HOST_APP_PATH" >
<RegistrySearch Id="HOST_App"
Root="HKLM"
Key="SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\HOST.exe"
Name="Path"
Type="raw"
/>
</Property>
<Property Id="HOST_ROOT_PATH" >
<RegistrySearch Id="HOST_Root"
Root="HKLM"
Key="SOFTWARE\HostApplication\Installation Info"
Name="HOST_Root"
Type="raw"
/>
</Property>
Windows Installer puts this search in AppSearch custom action.
Problem: AppSearch executes this search very early, before WriteRegistryValues of Host Installer, it won't get any values and properties with this search won't be defined, because registry to search was never written there.
Question 1: Can we reschedule this registry search from merge module after WriteRegistryValues of Host Installer?
Question 2: Is there any other way to search registry after Host Installer executes WriteRegistryValues? Probably with some custom action?
AppSearch is a standard action provided by the windows installer and by design is intended to run very early. This is because it's frequently used by the LaunchConditions standard action to decide if an installation can continue or not. It's also useful for deciding whether features and components should be installed.
MSI is a very opinionated framework. I suspect that there is something wrong with your current design that is going to be incompatible with MSI.
Is host installer the same MSI or a different MSI? Assuming it's the same, why couldn't you just put your data in some MSI properties and use those properties to write to the registry? Then you wouldn't need to read the values back in because you'd already have them in properties.
What do you need these properties for after writing them to the registry? Usually writing them to the registry would be the end game. I'm not sure what else you are doing next.
If host.msi is a different MSI, why are you having one MSI install another MSI? That's not MSI design. In this case you would need a bootstrapper. Host MSI would run first then this MSI. But even then it's kinda strange that a second MSI would depend on properties set by a first MSI. I'd think the bootstrapper UI and Application would gather this information and pass it as secure custom public properties to both MSIs.
To answer question 1: No a merge module can only insert actions into the sequence. It can't reschedule actions. 2: You would have to use a custom action. But as I said above, this feels like the wrong path to me.

How can I conditionally overwrite file during WIX install?

I have two work modes in my installer:
use config files left from previous installation
delete all existing configs and put default configs instead
The mode is determined by the checkbox in the WPF UI of the installer. If second mode is selected, then CustomAction is run, which manually deletes the configs folder from disk:
<InstallExecuteSequence>
<Custom Action="RemoveConfigsFolder" After="RemoveFolders" Overridable="yes">NOT Installed AND DELETESETTINGS=1</Custom>
</InstallExecuteSequence>
I'm using NeverOverwrite attribute:
<ComponentGroup Id="Configs" Directory="INSTALLDIR" >
<Component Id="Configs" Permanent="yes" NeverOverwrite="yes">
<File Id="main.config" Name="main.config" Source=".\Configs\main.config" KeyPath="yes" />
</Component>
</ComponentGroup>
The first mode works fine in this case, but when I try to use second mode it fails and all configs are just deleted and never created again during the installation.
During my research of the issue, I think I've found the reason why this happens: https://community.flexerasoftware.com/showthread.php?96157-The-truth-the-whole-truth-about-quot-Never-overwrite-quot-and-quot-Permanent-quot-files&p=156826#post156826
Actually this is a Windows Installer issue. If you log the uninstall
you will notice that very early in the installation the Installer
decides that the component containing this file will not be installed
because it is marked "Never Overwrite" and a copy of this file already
exists on the target machine. The uninstall happens after that which
removes the existing file. This is because the Installer decides this
when the "CostFinalize" action is launched. This action HAS to be run
before the "RemoveFiles" action.
But how do I fix it?
The problem with settings such as Never Overwrite or Permanent is they look like build settings, but they are not really - they stick to the system attached to the component id. So resetting in the project won't help because it's associated with that id. It's also not clear why setting Never Overwrite might have been a solution to some problem, because by definition patches and overwrite upgrades won't overwrite it, but overwriting it is a requirement of your setup.
Even if you had not set Never Overwrite the Windows Installer rules would not overwrite the file if it was modified after install. So if you had installed it, then it was altered, and then you did an upgrade, the file would not be overwritten (which is another reason why Never Overwrite does not seem needed).
Another issue is that your custom action RemoveConfigsFolder is not marked with an Execute enumeration value, therefore it is immediate, therefore it does not run elevated, therefore it might simply be failing, so without seeing the code it's impossible to say if reports an issue if it can't do the remove. It's also not possible to determine if it explicitly specifies the full path to the folder correctly. So the most likely quick fix to this issue is to mark the custom action as execute deferred, and the DELETESETTINGS value will need to be passed in via CustomActionData.
My initial thought is to remove the 'Never Overwrite' property. Then create a component condition that checks if the file exists. My thought is that your custom action has the condition to correctly remove the config files. If the files do not exist then the components will be selected for install.

WIX/MSI setup writes to HKLM despite MSIINSTALLPERUSER=1

I try to create a MSI Single Package using WIX (3.10). The user can select in the UI whether the package should install Per-User or Per-Machine. The package is initialized with ALLUSERS=2 and MSIINSTALLPERUSER=1. I did not set an InstallScope, as this would limit the Package to one or the other.
If the user selects Per-Machine the setup continues with ALLUSERS=2 and MSIINSTALLPERUSER="". - Everything works as expected.
If the user chooses Per-User installation ALLUSERS=2 and MSIINSTALLPERUSER=1 remain unchanged. If a non-privileged user runs the setup everything works as expected as well.
But if a privileged user executes the setup and chooses Per-User installation, registry keys that should be written to HKCU\Software\Classes\ are still written to HKCR / HKLM\Software\Classes\. This happens without UAC being invoked.
I observed this behavior on a Windows 8.1 (current patch level).
This is not what I want. If the user selects Per-User installation, there should be nothing written to HKLM/HKCR.
Here is sample from the wxs-file:
<Component Id="MyRegistryComponent" Guid="{99999999-9999-9999-9999-999999999999}">
<RegistryValue Id="MyRegistryComponent_MainKey" Root="HKCU" Key="Software\Classes\myapp.myclass" Value="myapp.myclass.foo" Type="string" />
</Component>
I checked the MSI with Orca. This RegistryValue has Root=1 in the Registry table of the MSI.
I tried/checked already:
I verified that the ALLUSERS and MSIINSTALLPERUSER properties are
indeed set as stated above.
I tried setting ALLUSERS="" MSIINSTALLPERUSER=1 – No effect.
I changed Root="HKCU" to Root="HKMU" in the wxs.
This results in Root=-1 in the MSI, but I does not change the final
result after installation.
What am I missing here?
Is possible that this behavior is caused by previous incomplete (un-)installations?
When you say "This results in Root=-1 in the MSI, but I does not change the final result after installation." it's not clear which of the results you're referring to but:
HKCU will always go to HKCU, HKMU is what you need to switch between HKCU and HKLM in a single package setup. So using HKMU and producing a verbose log would be useful to see if there is a failure somewhere in there.
You will get changes in HKCR. That's usually the normal behavior because as this article explains:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms724475(v=vs.85).aspx
Quote "The HKEY_LOCAL_MACHINE\Software\Classes key contains default settings that can apply to all users on the local computer. The HKEY_CURRENT_USER\Software\Classes key contains settings that apply only to the interactive user. The HKEY_CLASSES_ROOT key provides a view of the registry that merges the information from these two sources. HKEY_CLASSES_ROOT also provides this merged view for applications designed for previous versions of Windows."
HKCR is a virtual view that combines class registration for the current user with class registration for the machine to present a view of all class entries on the system. Other accounts (such as the system account) will see only HKLM, so what you see exactly depends on who you are.
I suggest that is what is needed here is a test case that uses HKMU as is required, and maybe post that test case. Keep in mind that seeing HKCR entries is normal, going by the documentation, so maybe that explains all that you are seeing.

How to modify xml on wix repair

I have an installer that is created with WIX and modifies a config via XmlFile, however I believe that the Wix Util Extension does not perform these actions on repair. This is causing problems when trying to perform a self-healing installer. Is there any way to accomplish what I am looking for
By piecing together a bunch of sources I came up with the following:
<Property Id="REINSTALLMODE" Value="amus"/>
<SetProperty Id="REINSTALL" Value="ALL" After="AppSearch">
<![CDATA[Installed AND REMOVE<>"ALL"]]>
</SetProperty>
Which forces a REINSTALL = ALL if it is not a remove or install
I have a similar scenario. Properties can be edited by the user through the UI, which are stored/loaded via the Registry and written to configuration files. Beyond Justin's answer, Secure="yes" must be set on each property, or MSI will ignore it (the log will show "Ignoring disallowed property").

Deleting files and directories before installation of MSI

Our product was installed via InstallShield Setup over the years. I changed the installation to MSI (WiX) this year. Now the MSI should clean up the directory, which remains the same.
One custom action in the MSI I implemented to start the uninstallation of the old product:
<CustomAction Id="UninstallIS" Property="QtExecCA" Value=""[WindowsFolder]IsUn0407.exe" -f "[ProgramFilesFolder]\company\product\Uninst.isu"" Execute="deferred" />
<CustomAction Id="QtExecCA" BinaryKey="WixCA" DllEntry="CAQuietExec" Execute="deferred" Return="ignore" Impersonate="no" />
After the removal of the old product there are temporary files and some subdirectories that are different from client to client and are unknown to the InstallShield Setup, so I would try to delete them via the MSI.
Keeping the UAC in mind, I think that I can't use command-line commands to do this also the 'Remove File Table' is not useful here (to much unknown files and many directories).
What is a possible way to do this?
Thank You for any help!
You can use a Deferred custom action which has Impersonate flag set to "no". This way it will run under the local system account with full privileges.
The custom action can use custom code (for example an EXE or DLL) or a command line.
Please note that deferred custom actions can be scheduled only after InstallInitialize action in InstallExecuteSequence.
As a side-note, make sure you thoroughly test it. Deleting files from the target machine is very dangerous. You never know what you may end up deleting.