Remove a folder if registry entry is not present - wix

I need to remove a folder if a certain folder path in registry doesn't exist. I wrote the below code but it doesn't work. The folder is not deleted and nothing about a condition check in the logs as well.
<Property Id="UPDATERSERVICEFOLDER">
<RegistrySearch Key="SYSTEM\CurrentControlSet\Services\UpdaterService\Parameters" Root="HKLM" Type="raw" Id="UpdateDirectoryRegistrySearch" Name="UpdaterServicePath" />
</Property>
<Component Id="RemoveFolder" Directory="MyProgramDir" Guid="*" >
<RemoveFolder Id="MosquittoInstallDir" On="uninstall"/>
<Condition><![CDATA[UPDATERSERVICEFOLDER]]></Condition>
</Component>
What am I doing wrong here? Any help would be much appreciated.

RemoveFolder won't remove the folder if there is anything in the folder when the component is processed. The issue you probably have is that this component gets processed before all the other components that represent something in that folder are processed or there are files that are not part of your install in this folder.
I don't know if there is a way to force a component to be processed last. An alternative you can try is Util:RemoveFolderEx. It is very important to note the remark at the bottom of this page. What this is basically saying is that you can't use a directory property to define the path to remove; you can't use [MyProgramDir] as the Property. The suggested way to implement the Property for this Util:RemoveFolderEx is to use a registry search to get the correct path then use that property set by the registry search.

Related

WiX Toolset: ICE03 Invalid identifier with RemoveFolderEx

I'm trying to create a .msi installer for a Node.js project using the WiX Toolset. I want to remove the node_modules folder during uninstall. This is the relevant excerpt from my .wxs file:
<Property Id='APPLICATIONFOLDER'>
<RegistrySearch Id='ApplicationFolder' Root='HKLM' Key='Software\[ProductName]' Name='InstallDir' Type='raw'/>
</Property>
<DirectoryRef Id='TARGETDIR'>
<Component Id='RemoveFolder' Guid='3e2231d9-61b3-41a2-b407-8df015be537e'>
<util:RemoveFolderEx Property='[APPLICATIONFOLDER]\node_modules' On='uninstall'/>
<RegistryValue Root='HKLM' Key='Software\[ProductName]' Name='InstallDir' Type='string' Value='INSTALLDIR' KeyPath='yes'/>
</Component>
</DirectoryRef>
I thought this would read the value from the registry, assign it to the property APPLICATIONFOLDER, and then in the RemoveFolderEx tag append the string \node_modules to that property's value.
But when I try to run this through light.exe, I get ICE03: Invalid identifier; Table: WixRemoveFolderEx, Column: Property, Key(s): wrf1A1445CB13E8BF98989EA24E3514470A.
Clearly, it is unhappy with my Property attribute, but what do I need to fix it? I've seen plenty of guides using this general pattern, but they always read the install dir value from the registry and then nuke that entire folder. That seems a little crass - if the user has added files to the directory I don't want to delete them. How can I specify a subfolder to delete?
I've solved the problem. Because I personally had a hard time figuring it out - the guides I've seen on RemoveFolderEx only all cover the same use case of nuking a folder saved to the registry, not a subfolder; and I find the WiX documentation to be less than beginner-friendly in most cases -, I am recording the solution here in case someone else should ever stumble upon the same problem.
As was pointed out already, Property does not work in the way I presumed in my question - it really does just accept the id of a property holding the path to the directory to delete. So you will definitely need to set a property in some way. (Some guides on the internet do not make this very clear.)
The next obvious path to investigate would be to assign the property a modified result of the registry search. However, this is impossible with WiX (to the best of my knowledge).
The answer lies in SetProperty. This, finally, allows to set a new property based on the one read from the registry.
It is worth noting that doing this still prevents the installation folder from being deleted, even if it empty. This, however, can easily be remedied by adding a RemoveFolder (without the Ex). This will delete the folder, but only if it is empty.
The relevant code that got it working for me is below.
<!-- Read the InstallDir from the registry and assign it to property APPLICATIONFOLDER -->
<Property Id='APPLICATIONFOLDER'>
<RegistrySearch
Id='ApplicationFolder'
Root='HKLM'
Key='Software\[ProductName]'
Name='InstallDir'
Type='directory'
Win64='yes'/>
</Property>
<!-- Set new property NODE_MODULES_FOLDER by appending node_modules\ to APPLICATIONFOLDER -->
<SetProperty
Id='NODE_MODULES_FOLDER'
Value='[APPLICATIONFOLDER]node_modules\'
Before='CostInitialize'/>
<Component Id='RemoveFolders' Guid='...'>
<!-- Remove the node_modules subfolder, using the property we created above -->
<util:RemoveFolderEx Property='NODE_MODULES_FOLDER' On='uninstall'/>
<!-- Remove main installation directory now, but only if it is empty. -->
<!-- That's why we're using RemoveFolder instead of RemoveFolderEx here. -->
<RemoveFolder Id='RemoveMainFolder' Property='APPLICATIONFOLDER' On='uninstall'/>
<RegistryValue
Root='HKLM'
Key='Software\[ProductName]'
Name='InstallDir'
Type='string'
Value='[INSTALLDIR]'
KeyPath='yes'/>
</Component>
I thought this would read the value from the registry, assign it to the property APPLICATIONFOLDER, and then in the RemoveFolderEx tag append the string \node_modules to that property's value.
It won't. As the doc says, Property is:
The id of a property that resolves to the full path of the source directory.
The easiest way to make that happen is to write the exact path you want to nuke to the registry.

WiX common component as merge module and INSTALLDIR from registry

I want to have 2 installers with common part. So I used merge module, and created 2 wix installers.
Here is what I want to achive with more details (problem described there was solved): wix installers with common component
I am using WixUI_InstallDir so user is able to choose directory where application will be installed.
When second installer is launched I want to load previously choosen installation directory.
When user does not change default path all works fine. But if INSTALLDIR was changed in first installation, then second installer adds plugin to right path but also extracts core to default path - which is wrong.
However right path (from previous installation) is shown on "Destination Folder" dialog.
Here is significant code:
<Property Id="CORE_INSTALLATION_PATH">
<RegistrySearch Id="InstallFolderRegistrySearch" Type="raw" Root="HKLM" Key="SOFTWARE\PluginCompany\Plugins" Name="InstallFolder"/>
</Property>
<SetDirectory Id="INSTALLDIR" Value="[CORE_INSTALLATION_PATH]">CORE_INSTALLATION_PATH</SetDirectory>
<DirectoryRef Id="TARGETDIR">
<Component Id="CoreRegistryEntries" Guid="{C1701385-12CA-47EF-9FB2-884139B56390}">
<RegistryKey Root="HKLM" Key="SOFTWARE\PluginCompany\Plugins" Action="createAndRemoveOnUninstall">
<RegistryValue Type="string" Name="InstallFolder" Value="[INSTALLDIR]" KeyPath="yes"/>
</RegistryKey>
</Component>
</DirectoryRef>
You can download and run full sample solution from https://github.com/bwojdyla/wixplugins/tree/04f61b89b0465311818bec1cc06371b3dced5671
In logs I found this:
MSI (c) (38:CC) [08:54:03:324]: Dir (target): Key: INSTALLDIR , Object: C:\Program Files (x86)\PluginCompanyFolder\PluginInstaller2\
MSI (c) (38:CC) [08:54:03:324]: Dir (target): Key: INSTALLDIR.751E70EB_CF76_413B_B8C8_231A31F9C946 , Object: C:\Program Files (x86)\PluginCompanyFolder\PluginInstaller\
So there are two properties INSTALLDIR and INSTALLDIR with guid.
This second property is added by merge module and updating INSTALLDIR does not change second property. That is why core components were extracted to custom location by second installer.
To disable mudularization I used SuppressModularization attribute:
<Property Id="INSTALLDIR" SuppressModularization="yes"/>
Notice SuppressModularization description at:
http://wixtoolset.org/documentation/manual/v3/xsd/wix/property.html
Use to suppress modularization of this property identifier in merge
modules. Using this functionality is strongly discouraged; it should
only be necessary as a workaround of last resort in rare scenarios.
A couple or three things:
Make sure you have the correct registry, maybe you need a Win64 search, maybe not, depends if the data is in the native registry or the x86 WOW one.
If it's not too late, it's typically better to install shared components to a common location (such as the common files folder for your Company Name and Product Name) to avoid this kind of thing. It's perfectly ok if not all the files are in the main application folder, as long as the app doesn't care.
Do the install with a verbose log and see what the AppSearch is doing - that's where it will set (or not) that property value. That's where you'll see if the property is being set or not, and therefore whether something else is going wrong later on.

Wix - How to get user input from a previously installed msi?

I'm trying to do an installer using wix 3.8. I can use custom properties to store my own input, but I'd like to use values that where inputs on a previously installed msi.
Is there a way to accomplish such thing?.
To get you in the right direction add this (of course adapt it to your needs first) in your fist MSI:
<DirectoryRef Id="INSTALLDIR">
<Component Id="RegistryEntries" Guid="{0AC76129-F8E2-47D3-B9FD-09B1E10A8541}">
<RegistryKey Root="HKLM" Key="Software\Company123\App123" Action="create">
<RegistryValue Type="integer" Name="SomeIntegerValue" Value="1" KeyPath="yes"/>
<RegistryValue Type="string" Name="UserInput" Value="[USERINPUT]" />
</RegistryKey>
</Component>
</DirectoryRef>
Don't forget reference the component in your <Feature> <ComponentRef Id="RegistryEntries" />
When you install assign a value to the property [USERINPUT] e.g. msiexec /i your.msi /qb+ USERINPUT="the value to be saved in registry"
Then in the second MSI add something like this:
<Property Id="READREGISTRY">
<RegistrySearch Id="USERINPUT_Value" Root="HKLM" Key="Software\Company123\App123" Name="UserInput" Type="raw" />
</Property>
The value/string you entered during installation USERINPUT= will be stored in your second MSI in the property READREGISTRY
Here a piece of log in my second msi:
PROPERTY CHANGE: Adding READREGISTRY property. Its value is 'testing registry wef wef wef w'.
Based on your installation where it might be Per User or Per Machine you may adjust the Root to HKCU for PerUser installs or leave it to HKLM for PerMachine.
For more information please refer to Wix documentation, hints: "How To: Write a Registry Entry During Installation" and "How To: Read a Registry Entry During Installation".
Generally, no. There is no requirement for a Windows Installer package to record the input it takes from the user. Some do record some information in the registry and you might choose to rely on finding it there.
As an alternative, you might find that the other installer can be run without a UI and can be sufficiently controlled with properties passed into it. If so, you can write your own UI (one way would be a custom WiX Bootrapper Application [example]) to gather the inputs before running the installer.
Create a custom action within the MSI which gets installed first, then either write the values/user entries that you want into a file or registry. Within your final MSI read the values from registry/file and use it.
Here is an example of how you can read a value from the user and update the your app.config, this is not an apple to apple scenario, but this will guide you through it.
http://bensnose.blogspot.com/2013/03/more-custom-actions-with-wix.html
Disclaimer: I havent tried out what is mentioned in this blog post, but I have done things very similar and found that it has good explanation, thats why I posted the link to it.

Setting permissions for folder and all sub folders in Wix

All of the relevant links seem to assume that I am using DirectoryRefs and CreateFolder within these. When I run heat I get a .wxs file that consists of <Directory> tags and <Component> tags but not DirectoryRefs. I'd rather not rewrite the whole 5000 line file by hand. Is there a way to edit folder permissions for these tags?
You don't have to rewrite the heat auto-generated fragment. You can reference the directories defined there with the DirectoryRef elements in another fragment.
Most likely you'd like to do the following:
First, change the ID of the root directory in a heat-generated fragment. This can be done by a -dr <DirectoryName> command line switch of heat.exe. Choose a name for the DirectoryName, e.g. MY_ROOT_FOLDER. This is necessary for a convenient reference to that directory in a DirectoryRef element
Next, author a special component (in a different fragment), which is to contain the permissions functionality only. Something like this:
<DirectoryRef Id="MY_ROOT_FOLDER">
<Component DiskId="1" Id="Permissions" Guid="GUID-GOES-HERE">
<Condition>...</Condition>
<CreateFolder>
<util:PermissionEx GenericAll="yes" User="[LOGON_ACCOUNT]" Domain="[LOGON_DOMAIN]" />
</CreateFolder>
</Component>
</DirectoryRef>
Finally, don't forget to include this component to a feature of your choice.
Note, that by default the <util:PermissionEx> element appends permissions to the folder in question and all its descendants.

Using Wix how can I deploy one of several web.config files while installing an ASP.net web application

I'm using Wix 3.6 beta from the command line, not as VS projects. I have a web application that is harvested with heat as a directory. This works. I'm using web.config transforms to manage each of the target environment web.config files. These are output with msbuild, this works and keeps things visible in Visual Studio and source control.
I've hit a problem deploying one of the several web.config files which I am manually including in product.wxs as components with conditions. I was expecting to include all components as deployable features and let the conditions select just one as active. For example:
<DirectoryRef Id="wwwroot">
<Component Id="setup_a" Guid="some_guid" >
<File Source="$(var.ConfigSourceDir)\setup_a\web.config" />
<Condition>ENVIRON = setup_a</Condition>
</Component>
<Component Id="setup_b" Guid="some_guid" >
<File Source="$(var.ConfigSourceDir)\setup_b\web.config" />
<Condition>ENVIRON = setup_b</Condition>
</Component>
This didn't create any file renaming, moving or deleting issues, but has the very fundamental problem that multiple web.config files are mapped to the same destination and this gives me a light error of "Product.wxs(xxx) : error LGHT0091 : Duplicate symbol 'File:web.config' found. This typically means that an Id is duplicated. Check to make sure all your identifiers of a given type (File, Component, Feature) are unique."
An alternative approach was to use different named .config files and rename/move one to be the web.config, so something like:
<DirectoryRef Id="wwwroot">
<Component Id="setup_a" Guid="some_guid" >
<File Id="setup_a.config" Source="$(var.ConfigSourceDir)\setup_a.config" />
<CopyFile Id="moveit" SourceDirectory="wwwroot" SourceName="setup_a.config" DestinationDirectory="wwwroot" DestinationName="web.config" />
</Component>
This doesn't throw an error, bot the CopyFile command does nothing at all. I just get setup_a.config in the wwwroot folder.
If I nest the CopyFile inside the File, the copy action then works:
<DirectoryRef Id="wwwroot">
<Component Id="setup_a" Guid="some_guid" >
<File Id="setup_a.config" Source="$(var.ConfigSourceDir)\setup_a.config" >
<CopyFile Id="moveit" DestinationName="web.config"/>
</File>
</Component>
...but nested CopyFile means I can't add (it's disallowed) the Delete="yes" attribute to create a 'move' action. Instead I'm left with both setup_a.config and web.config in the wwwroot folder. Alternatively, if I add a seperate removefile within the same component element it also does nothing:
<RemoveFile Id="removefile" On="install" Directory="wwwroot" Name="setup_a.config"/>
</Component>
So, I'm hoping for a working example of how handle multiple web.config files in a conditional deployment, that doesn't leave files behind. the destination filename of web.config is fixed by the framework and can't be changed. The different configs are also pre-generated outside of wix using config transforms, this also can't be changed but the generated filenames could be anything.
cheers!
You complicate it too much. This should work:
<Component Id="setup_a" Guid="some_guid" >
<File Name="web.config" Id="config_a" Source="$(var.ConfigSourceDir)\setup_a\web.config" />
<Condition>ENVIRON = setup_a</Condition>
</Component>
<Component Id="setup_b" Guid="some_guid" >
<File Name="web.config" Id="config_b" Source="$(var.ConfigSourceDir)\setup_b\web.config" />
<Condition>ENVIRON = setup_b</Condition>
</Component>
Pay attention to a couple of things here:
the File/#Name is the same - that's the target file name you'd like to have (web.config)
the File/#Id is different for each File in order to avoid the light error you mentioned first
the File/#Source can be anything - it just describes what file to take as a source
In this sample light will still complain with warning LGHT1076, but that's just a warning - it pays your attention that conditions MUST be mutually exclusive to avoid problems.
I usually take a different approach. Rather then placing multiple mutually exclusive files into an installer that are tightly coupled to specific instances I put a generic file in the installer and use XML changes to transform the XML with the variation point data such as connection string and what not.
This allows me to make installers that can be deployed anywhere silently just by passing a few properties and the command line.