Wix toolset: registry key not deleted after uninstall - wix

I'm using wix to write an installer for my x64 app. So the wxs looks like this:
<Package Id="*" Platform="x64" ...
And I'm running candle with -arch x64. However, I need to write a registry key to communicate with a 32 bit app under Software\WOW6432Node\MyCompany\MyKey, so I have a component like this:
<Component Id="SomeId" Guid="SomeGUID">
<RegistryKey Root="HKLM" Key="Software\WOW6432Node\MyCompany\MyKey">
...
Things seem to work fine for installation. However, when I uninstall, the registry key is not removed. I suspect this is not the correct way to add a registry key under Software\WOW6432Node. My search on SO and Google haven't produced any results though. Any suggestions? Thanks.

WiX RegistryKey element documentation:
https://wixtoolset.org/documentation/manual/v3/xsd/wix/registrykey.html
Description
Used for organization of child RegistryValue elements or to create a registry key (and optionally remove it during uninstallation).
ForceDeleteOnUninstall YesNoType Set this attribute to 'yes' to remove the key with all its values and subkeys when the parent component is uninstalled. Note that this value is useful only if your program creates additional values or subkeys under this key and you want an uninstall to remove them. MSI already removes all values and subkeys that it creates, so this option just adds additional overhead to uninstall. The default is "no".
The otherthing to consider is this only happens if the component is being removed. Logging the uninstall will give you more information on what's going on.

Related

Make Wix to not uninstall common dll

I have Wix project in which I need a common used dll library to be installed if it's absent.
If this dll exists I should not overwrite it.
So, when I set DefaultVersion="0.0.0.0" this dll is not overwritten if it exists, its ok. But when I delete app, the dll is beeing removed. How do I prevent removing dll in the case when it existed before installation?
I don't want to make it permanent because it should be removed if it didn't exist before installation.
<Component Id="myLib.dll" Permanent="no" Directory="Shared_Dir">
<File Name="myLib.dll" KeyPath="yes"
Source="mySource\myLib.dll"
DefaultVersion="0.0.0.0"
/>
Add reference to WixUtilExtension and xmlns:util="http://schemas.microsoft.com/wix/UtilExtension" attribute to Wix element in your code.
Define <Property Id="Dll_Installed" Value="false"/> in Product element.
Add child <Condition>NOT Dll_Installed</Condition> to component myLib.dll.
Add that somewhere in your code:
<Fragment>
<util:FileSearch
Id="Dll_Installed"
Variable="Dll_Installed"
Path="[Shared_Dir]myLib.dll"
Result="exists"/>
</Fragment>
DefaultVersion attribute is not necessary.
The feature you are describing is reference counting. The Windows Installer reference counts with Components. Components are identified by their GUID.
So the normal way to address this requirement is to put the File in a Component and make sure the GUID of the Component is stable. The WiX Toolset should do exactly that automatically if if you do not specify the Component/#Guid attribute.
So the default behavior should just work for you.
The only other piece of the puzzle is the Windows Installer will install the latest version of a file. If the file version is the same or less the file will not be installed but will be reference counted.
Based on the details in the question it seems like you should be just fine with:
<Component Directory="Shared_Dir">
<File Source="mySource\myLib.dll" />
</Component>
One might ask why the Windows Installer use Components to reference count files. We'll, it allows you to group other resources, like registry keys, together and control their install as a unit. Much more important if you are installing COM servers than plain old files.

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."

Wix Burn: How to store the custom InstallFolder for later modifications?

I'm trying to solve this for a while now. I've authored a custom UI for my Bootstrapper Application. Setting a default value for InstallFolder is not the problem, but when the user changes this path, how can I store this path for later changes in add/remove programms, e.g. when another Package in the bundle should be installed by modifying the Bundle?
To write in the Registry could be an option, but the Bootstrapper Application doesn't run elevated all the time, so that it can't write to HKLM. But there should be a way to do this, I saw similar things for Visual Studio...
You should be storing the InstallFolder value in the registry in one (or all depending on how it is authored) of your MSIs that are packaged with the bootstrapper application. On startup you can use a util:RegistrySearch to look for and set the InstallFolder in the bootstrapper.
<Fragment>
<util:RegistrySearch
Id="ServerInstalledCheck"
Root="HKLM"
Key="SOFTWARE\$(var.OEMRegistryRootKeyName)\v7"
Value="ServerPath"
Result="value"
Variable="ServerInstalled"/>
<util:DirectorySearch
Path='[ServerInstalled]'
Variable='InstallFolder'
After='ServerInstalledCheck'
Condition='ServerInstalled' />
</Fragment>
I think you can directly set the variable InstallFolder in the registry search itself and omit the DirectorySearch. The DirectorySearch approach was used just to ensure we only set the InstallFolder to a location that actually exists on the machine. There may be other advantages as well but I can't think of them at the moment.
This will retain your default InstallFolder location on a fresh install and 'remember' the selected install location when running to uninstall/modify/upgrade.
You are right that you cannot rely on writing any registry keys inside your bootstrapper application because it is not guaranteed (and really shouldn't be) run elevated.
This is basically following the 'remember property' pattern which is explained here. Whenever you want to remember a value set in a previous install during modify/upgrade/removal, this is generally the go to.

How to modify the registry key value on uninstall without removing the key from registry in wix?

I added a registry key value into the registry on install.And I want to modify that registry key value on uninstall but I don't want to removing that from registry.
1)I tried with custom actions execution on uninstall to modify the registry value. But the registry values are removing from the registry on uninstall.
2)If I make the component to permenent then it is not modifying the key value at the time of uninstall.
<Component Id="SampleRegComp"
Guid="3865FE52-F8EE-4E29-B321-BDF0FD6D3F58"
Permanent="yes">
<RegistryKey Action="create"
Key="SOFTWARE\Microsoft\Notepad"
Root="HKCU">
<RegistryValue Name="StatusBar" Type="integer" Value="1" />
</RegistryKey>
</Component>
<CustomAction
Id="ModifyOutlookRegInitSign_12"
Return="ignore"
Directory="TARGETDIR"
ExeCommand= ""[SystemFolder]reg.exe" ADD "HKCU\SOFTWARE\Microsoft\Notepad" /v StatusBar /t integer /d 0 /f" >
</CustomAction>
Installed
The short answer is to create the registry entry with your code and modify it with your code, then it's clear that you are managing those entries, not both you and the MSI setup.
An alternative is to put them in a component by itself with a null component guid, then MSI won't manage it at all after it's been installed.
Phil has already answered, but maybe I can add that you can use (REMOVE="ALL" AND NOT UPGRADINGPRODUCTCODE) for the custom action to be executed during uninstallation. Then just set the registry component permanent (HKCU registry data shouldn't really be uninstalled either - a matter of some debate though). This still won't fix the HKCU data for users who are not logged on at the time of the uninstall though. This is a much more involved task, probably possible to do with ActiveSetup.
Adding NOT UPGRADINGPRODUCTCODE ensures uninstalls performed as part of major upgrades do not update the registry since the product is then due to be reinstalled. Shouldn't be necessary for your case, but ensures the custom action doesn't run unnecessarily.
I would suggest scheduling the custom action right before InstallFinalize in the InstallExecuteSequence. I haven't tested this location, but it is then one of the last actions to run during the uninstall, and should have the desired effect.
See the condition lists for detecting different installation modes at the bottom of this thread.

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.