I am writing an installer which needs to lookup the installation directory from a registry value.
I am using the property to lookup the registry and the component group refers that property. But the component group doesn't seem to be picking up directory from the property. Is there a better way to do it?
Cheers..
I guess you are doing something like that :
<Property Id="MY_PATHNAME" Secure="yes">
<RegistrySearch Id="My_Path_Signature" Root="HKLM"
Key="$(var.My_Key)" Name="PathName" Type="directory">
</RegistrySearch>
</Property>
then you're trying to retrieve the value of "MY_PATHNAME"...
In case the installer decides not to set the property correctly , you can generate a log from the command line :
msiexec /i MyInstaller.msi /l*v MyLog.txt
Related
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.
I want to know if in the same directory of my setup (CURRENTDIR) there is a file named "pippo.txt". So I made the following property.
<Property Id="TROVAPIPPO">
<DirectorySearch Id="trovadir" Depth="0" Path="CURRENTDIR">
<FileSearch Id="trovafile" Name="pippo.txt" />
</DirectorySearch>
</Property>
Now I want to display the value of this property in the log file (in temp folder). But I don't know how.
Run your installer from the command line with msiexec /i yourInstaller.msi /L*v yourLogFile.txt
Changes to MSI properties will be logged. The log files are pretty big, but it'll be in there somewhere.
I have the following WiX excerpt which works perfectly during an install but doesn't during uninstall. I'm quite new to WiX so apologies if it's a glaring error. Can anyone see the problem?
<Property Id="APPFOLDER" >
<RegistrySearch Id="APPInstalledFolderReg"
Root="HKLM"
Key="SOFTWARE\$(var.MANUFACTURER)\One Of My Web Services"
Name="Folder"
Type="raw"
Win64="yes" />
</Property>
<CustomAction Id="SetAPPFolderProperty"
Property="APPInstalledFolder"
Value="[APPFOLDER]"
Execute="immediate"
/>
<InstallExecuteSequence>
<Custom Action="SetAPPFolderProperty"
After="AppSearch"
>APPFOLDER</Custom>
<Custom Action='MyUnregisterAction'
After='InstallInitialize'
>REMOVE="ALL"</Custom>
</InstallExecuteSequence>
During "MyUnregisterAction", the APPInstalledFolder is not set. The MsiExec Install Log reports:
Skipping action: SetAPPFolderProperty (condition is false)
I understand this means the CustomAction's not executing because the Property APPFOLDER is not set. Is the RegistrySearch not performed during Uninstall?
The Registry search is definitely performed during an uninstall.
As a matter of fact, i tried your same snippet and could get it to work.
Are you running the installation in full UI mode?
In that case, the registry search will be run in the UI mode. Probably, what is happening here is that , the value of the property APPFOLDER is not passed into the Execute sequence.
For the value of the property to be passed to the execute sequence, you need to secure the property ie. add the APPFOLDER property to SecureCustomProperties in the Property table.
Once you do this, you code snippet should start working.
In general, under normal circumstances, the values of public properties are passed from the UI sequence to the execute sequence. However, there could be lockdown conditions where the property needs to be explicitly added to the SecureCustomProperties list in the Property table for the value to propogate to the Execute sequence.
Hope this helps.
Regards,
Kiran Hegde
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.
I have written following code in my wix installer for updating the language in an XML file and this works fine in following conditions:
1) fresh install
2) upgrade by using admin rights (I am using msiexec /I "Tools.msi" /l*v D:\InstallLog.txt).
However, when I run/execute the msi without admin rights (This is where "Change" mode is being used....what am I missing) the values are not updating always set to the default value :
<Property Id="LANGUAGE" Value="en-US" />
The code:
<util:XmlFile Id="LanguageConfig"
Value="[LANGUAGE]"
ElementPath="/configuration/appSettings/add[\[]#key='Language'[\]]"
Action="setValue"
File="$(var.ConfigFile)"
Name="value" Sequence="1" />
<util:XmlFile Id="MappingLanguageXML"
Value="[LANGUAGE]"
ElementPath="/MappedUsers/UsersList/UserID/LanguageCode"
Action="setValue"
File="$(var.UserMappingFile)" Sequence="1" />
Any help is much appreciated.
The fact that it's being changed implies it may not be a privilege issue. I suspect that the scenario is that a different language was entered at install time, but you did not preserve its value anywhere so Change just uses the default value. Properties aren't remembered automatically, that's what the WiX "remember property" pattern is for. That's likely to be the solution - preserve the install time value of LANGUAGE. It's no different from running any program - the fact that the user may have entered something that went into a variable doesn't mean that the variable will have that same value next time you run the program unless you save it.
Based on PhilDw pointer I solved the issue by modifying my property
<Property Id="LANGUAGE" Value="en-US" />
as follows:
<Property Id="LANGUAGE" Secure="yes" Value="en-US">
<RegistrySearch Id="RememberLanguage" Root="HKLM"
Key="$(var.RegistryLoc)"
Name="Language" Type="raw" />
</Property>
By preserving the value in the registry I was able to modify the value during "Change" operation.