Is there any way to find (and execute) a file that lies near installer ? My example of file structure:
MyInstaller.msi
Tools
Tool.exe
I want to be able to find Tools\Tool.exe from MyInstaller.msi and launch it (if it exists) when user will click a button. I'm trying this
<Property Id="TOOL_EXE">
<DirectorySearch Id="TOOL_PATH" Path="\Tool\">
<FileSearch Id="TOOL_EXE_SEARCH" Name="tool.exe" />
</DirectorySearch>
</Property>
but TOOL_EXE variable is empty.
Important note: I don't have this file in moment of creating installer, so I can't include it.
I think the only problem here is the Path-attribute, where you specified \Tool\ as value. The leading backslash tries to search on the root, therefore it can't find the directory. Also leave away the final backslash: adding it the installer expects something to follow there. So try .\Tool instead.
You could also use the following (SourceDir is referencing the folder of the installation file, see also here). It is possible though that the property is not already available, didn't test this.
<Property Id="TOOL_EXE">
<DirectorySearch Id="TOOL_PATH" Path="[SourceDir]Tool" Depth="0">
<FileSearch Id="TOOL_EXE_SEARCH" Name="tool.exe" />
</DirectorySearch>
</Property>
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 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.
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 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.
In my Wix project, I need to set an external application as the default program for a new file type, so I add some file associations in registry keys(weird I know, but I'm developing a plugin and I don't find a native way to deal with external programs).
MSDN says we should call SHChangeNotify if we change file associations. If not, the new association will not work until the system restarts. Here is my problem, how can I do this in Wix? I find a tool which implements this feature but what I need is hard code this in Wix Installer.
[solution] At first I add ProgId element the way #BdN3504 shows. Then I use Custom Action to send SHChangeNotify. Cheers~
Have you had a look at this answer?
You have to first locate the target application in a FileSearch and then reference that in an Extension element.
<Property Id="TARGETEXE">
<DirectorySearch Path="C:\Program Files (x86)\App"
Depth="0"
AssignToProperty="no"
Id="NppSearch">
<FileSearch Name="Target.exe"
Id="targetExeFileSearch" />
</DirectorySearch>
</Property>
<ProgId Id='Fileassoc.assoc' Description='File extension description'>
<Extension Id='assoc' ContentType='application/assoc'>
<Verb Id='open' Command='Open' TargetProperty='TARGETEXE' Argument='"%1"' />
</Extension>
</ProgId>
See the documentation for the
DirectorySearch Element
FileSearch Element
ProgId Element
Extension Element
and the Verb Element