WiX Preserve registry during major upgrade but remove on uninstall - wix

I would like to preserve all registry values (all contained within a single key) during an MSI major upgrade, but I would like the values deleted (as usual) when the application is uninstalled.
I believe I need to use RegistrySearch to achieve this, reading the values individually to properties.
I understand this can be achieved using the following markup:
<Property Id="EXISTINGAPIKEY">
<RegistrySearch Id="name_of_dword"
Root="HKLM"
Key="Software\SomeCompany\SomeApp"
Name="api_key"
Type="raw" />
</Property>
I have now saved the value of HKLM:\Software\SomeCompany\SomeApp\api_key to a property called EXISTINGAPIKEY.
The next step is to write this value back to the registry once the upgrade has completed, but before the service (which is installed/updated by the package) is started.
So the questions are:
How can I write this property back to the registry after the update but before the service is started?
How can I ensure this only happens during an upgrade, and that the registry is cleaned during an uninstall?
A good chance that I'm over-thinking this!

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.

Wix Toolset RegistrySearch is not saving a registry key

I have an application that is saving multiple registry keys on install to ensure that certain choices a user makes are persisted in the application. Most of these keys work correctly, but some do not record an entry. Am I doing something wrong? or is there a limit on the WiX RegistrySearch? I have checked the documentation and can't find any limit. I have included some examples below :
The following works correctly:
<Property Id="SERVER_NAME">
<RegistrySearch Id='rsSERVER_NAME' Root='HKLM' Key='SOFTWARE\CompanyName\ApplicationName' Name='ServerName' Type='raw' />
</Property>
The following does not work correctly:
<Property Id="SERVER_NAME">
<RegistrySearch Id='rsSERVER_NAME' Root='HKLM' Key='SOFTWARE\CompanyName\ApplicationName\ApplicationDetails\Servers' Name='ServerName' Type='raw' />
</Property>
Is there any reason why the 2nd code block would not work?
If you want to persist property values from user choices it may be easier to just let WiX do it with the "remember property" pattern.
It's not clear how you are saving these values and retrieving them, because there is no indication if you are saving them in 32-bit or 64-bit location, or if you are using the -arch switch to set the default, so it may be that you are saving or restoring them from different bitness locations (see RegistrySearch win64 setting). Without this context it's not clear if that search will work or not. It appears to be a 32-bit search in the absence of Win64=yes, but the -arch switch changes the default.
TEST OK: I ran a test of this and both values were retrieved from the 32-bit section of the registry (HKLM\SOFTWARE\WOW6432Node) without any problems.
Here is the WiX RegistrySearch documentation. And below are the registry paths mentioned by Phil for 64-bit and 32-bit applications - your WiX code specifies 32-bit, so you read from the WOW6432Node section:
HKLM\SOFTWARE (for 64-bit applications)
HKLM\SOFTWARE\WOW6432Node (for 32-bit applications - believe it or not)
I am wondering if you are just mixing up the paths in the registry? Here is where I am reading from - it is in HKLM of course (I cropped the screenshot a bit to make it fit the page):
UPDATE: I have now tested this reading from both the 32-bit and 64-bit sections of the registry. It works as expected as far as I can see? There must be an error in your registry path somewhere?
I use a one-line VBScript to show the property value after the RegistrySearch has run and retrieved the properties. I can update this answer to add this code if you want.
It is a little unclear what you mean when you say that an application is saving multiple registry settings during installation. Is this a custom action you are running which writes these registry keys, and then your setup reads them back?
It is unclear how these values - that you read back from the registry using RegistrySearch - are actually written to the registry? Perhaps they are from a prior version of your application or from another application and you want to "copy" them? If so, can you verify whether they are HKCU or HKLM settings? For HKCU settings I would prefer to do the copy in the application itself for reliability reasons.

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

MSIEXEC using command line REINSTALL not using original INSTALLDIR

I am trying to configure Wix to build my msi to only perform build versions (1.0.x) of my product in conjunction with the REINSTALL property, my problem is that when I run the command line: MSIEXEC.exe /i my.msi /l*vx build-inst.log REINSTALL=ALL REINSTALLMODE=vamus it fails to do anything.
I have checked the msi log and found that it is looking for the existing product in the default folder (.\program files (x86)...\myproduct) yet when I installed it the first time I actually used a custom path (c:\myproduct). It was my impression that using REINSTALL the installer would use the installed path of the original product.
Is this actually the case? Should I be specifying the INSTALLDIR on my command line? I would rather not as this is meant for use by clients and I cannot guarantee I will know where the product was installed.
This method of performing "build" upgrades has been suggested in a couple of places but I can not find anything explaining any need to specify the INSTALLDIR
Is there any way to configure this in Wix?
Thanks
Kieran
The easiest solution would be to store the installation directory in the registry and look it up upon reinstalling.
To look up your registry value, you'd use something of the sort:
<Property Id="INSTALLDIR">
<RegistrySearch Id="InstallLocation" Root="HKCU"
Key="SOFTWARE\Company\Product" Name="Location" Type="raw" />
</Property>
If the registry value isn't found, the INSTALLDIR property will be set to your directory structure.
Rob has a complete solution on his blog for when you specify such a property from the command line.
Normally the original entries in the directory table are stored for reinstall without that you store them yourself.
So there is something "special" in your MSI, if this doesn't work. If you have a custom action which sets directory properties like INSTALLDIR, you should not use it. E.g. give them a condition "Not Installed".
I found out that the problem was due to using a wildcard for the product id, so every time a new msi was built it created a new product id.
By fixing this it seemed to resolve the problem, though I have also implemented the registry key option as it will help for upgrades where I do want to change the product id.
Thanks

WiX Changing UI behavior based on registry value

The short version is that I am building an installer package, and I want the “Next” button of my welcome screen to change its behavior based on whether or not a specific registry key exists. It seems like I should be able to modify the conditions of the next button’s actions to get this behavior, but so far no luck. I always either get behavior A or behavior B, I have not gotten behavior that is sensitive to the registry value.
I should note that I can remove the UI completely and run the package via command line to get the desired results, but I am trying to be a little more user friendly by adding the install path dialog when appropriate.
In a bit more detail…
I am working on installers for a series of independent, but related, class libraries. If we install a library on a clean box I want the installer to prompt the user for an install path, and then write this path off to the registry during the install process. However if we install a library on a box which already has one of the other libraries present, I want the installer to read the registry and use the same path as the previous install.
My thought process was to modify the standard WixUI_InstallDir interface to check for the registry search result and skip the InstallDirDlg if it is set. However this does not appear to be working. Here are some snips from the XML:
<Property Id="FOOPATH">
<RegistrySearch Id="PathSet" Type="directory" Root="HKLM" Key="Software\Foo" Name="InstallPath"></RegistrySearch>
</Property>
<Property Id="PATHSET">
<RegistrySearch Id="PathSet" Type="directory" Root="HKLM" Key="Software\Foo" Name="InstallPath"></RegistrySearch>
</Property>
<Directory Id="FOOPATH" Name="Foo">
<Component Id="FooPathReg" Guid="Some Guid">
<RegistryKey Root="HKLM" Key="Software\Foo" Action="createAndRemoveOnUninstall">
<RegistryValue Name="InstallPath" Type="string" Value="[FOOPATH]" KeyPath="yes"></RegistryValue>
</RegistryKey>
</Component>
</Directory>
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="InstallDirDlg">NOT Installed AND NOT PATHSET</Publish>
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg">NOT Installed AND PATHSET</Publish>
If my publish conditions are using the PATHSET property (as shown) then I always get the install directory dialog, if I switch them to use the FOOPATH property then I never get the dialog. I have also tried playing with the InstallUISequence and it does not seem to matter how early I schedule the AppSearch action, I still get the same result.
Is there something simple I am missing? Or do I need to take another approach on this?
Check a verbose log to see if the properties are set as you expect.
Use Orca to see if the ControlEvent is as you expect. UI is additive, so you have to take extra steps when you want to replace the stock UI. For example, see http://neilsleightholm.blogspot.com/2008/08/customised-uis-for-wix.html.
Ok this turned out to be one of those brain dead moments where I missed something in my testing process. I am filling in the details of why it was failing in the hope that it saves someone else some of the frustration I have experienced while working on it.
First it is important to know that I was not actually testing multiple packages, I was trying to get my base structure worked out with the first package, then apply it to the rest when I thought I had it fairly close, this was to help reduce the number of changes I needed to replicate across the group of packages. To do this I was manually adding the registry entry in question, prior to running the package, there by simulating a prior install.
What I neglected to do was actually create the directory referenced by the registry entry. I was walking through the process with a co-worker, and in the course of explaining it I noticed this section of the log:
Action start 8:26:16: AppSearch.
MSI (c) (BC:D4) [08:26:16:505]: Note: 1: 2262 2: Signature 3: -2147287038
MSI (c) (BC:D4) [08:26:16:506]: Note: 1: 2262 2: Signature 3: -2147287038
MSI (c) (BC:D4) [08:26:16:507]: Note: 1: 2262 2: Signature 3: -2147287038
MSI (c) (BC:D4) [08:26:16:507]: PROPERTY CHANGE: Adding NETFRAMEWORK35 property. Its value is '#1'.
Action ended 8:26:16: AppSearch. Return value 1.
And it occurred to me to try creating the directory referenced by the registry value as well as the registry value itself. Once the directory was there, everything started working properly.
Apparently when you tell the RegistrySearcher that the value is a directory, it only sets the value into the property when said directory actually exists. This detail never came up in my searches on the topic, and is not clear in the documentation I have found, though in retrospect I can see where it is implied.