WIX does not remove shortcuts in the INSTALLDIR if not default - wix

Why WIX does not remove a shortcut in the INSTALLDIR if it is not the default install directory is used? My WIX code look like?
<DirectoryRef Id="INSTALLDIR">
<Component Guid="..." Id="shortcuts_INSTALLDIR">
<RegistryKey ForceDeleteOnUninstall="yes" Id="shortcuts_reg_INSTALLDIR" Key="Software\MyCompany\MyProduct" Root="HKCU">
<RegistryValue KeyPath="yes" Name="shortcut_INSTALLDIR" Type="string" Value=""/>
</RegistryKey>
<Shortcut Arguments="my args " Description="my description" Id="InstallDir_my_name" Name="my name" Target="[INSTALLDIR]mydir\my.exe" WorkingDirectory="INSTALLDIR"/>
</Component>
</DirectoryRef>
It look like that the uninstaller does not know the new value of INSTALLDIR. Any idea?

Windows Installer is a bit of an odd beast here. It doesn't record the operations it performs; instead it tries to record the information necessary to reverse them. In this case it appears you're falling into a gap in that implementation.
Windows Installer notes that it has installed component shortcuts_INSTALLDIR. When a file is installed to a specific directory, it records the directory's location. Then during maintenance it restores all the directories it recorded. But it does not record (and thus does not restore) the directory for just a shortcut. Typically shortcuts are installed to predefined paths under the ProgramMenuFolder. Since such locations are not affected by changes to INSTALLDIR, this is usually not a problem.
To solve this you have to ensure the alternate INSTALLDIR is restored during maintenance. You can convince Windows Installer to do so automatically by installing any file directly to INSTALLDIR (if the extra file is not a problem, this is my preferred option). Alternately you can do so manually through the remember property pattern, possibly leveraging ARPINSTALLLOCATION and its saved value in the Uninstall key.

Related

Old Property with RegSearch is affecting Product upgrade (new version without this prop)

I've inherited project with MSI created in WiX and now I'm trying to solve some of the issues that unfortunately exist.
There's a remember property pattern which is used to found specific directory saved in registry entry:
<Property Id="AUTO_FOUND_DIR" Secure="yes" Admin="yes">
<RegistrySearch Id="regsrch_AUTO_FOUND_DIR"
Root="HKCU"
Key="$(var.RegPath)"
Name="$(var.SpecificKey)"
Type="raw"
/>
</Property>
The SpecificKey value is then saved in AUTO_FOUND_DIR property.
Then the black magic appears. A separate component is holding (among other stuff) a shortcut located in ProgramMenuFolder (non-advertised) to the main executable.
I've been told that usage of util:RemoveFolderEx is a workaround for an old issue where this shortcut was orphaned and hasn't been removed during uninstall:
<Feature>
<DirectoryRef Id="ProgramMenuDir">
<Component Id="cmp_ProgramMenuDir" Guid="{0E8BD13A-GUID-IS-HERE-6E5092ECA9EF}">
<CreateFolder />
<RemoveFolder Id='ProgramMenuDir' On='uninstall' />
<RegistryKey Id='reg_SpecificKeyID' Root='HKCU' Key='$(var.RegPath)' ForceCreateOnInstall="yes">
<RegistryValue Type='string' Name='$(var.SpecificKey)' Value='[ProgramMenuDir]'/>
</RegistryKey>
<!-- other content: shortcut to ProgramMenuFolder and other stuff -->
<util:RemoveFolderEx Id="rm_dirID" On="install" Property="AUTO_FOUND_DIR"/>
</Component>
</DirectoryRef>
</Feature>
The problem is: I don't need this workaround (and usage of AUTO_FOUND_DIR property as well. I've removed that code but during upgrade (major, Product and Package GUIDs set to "*", UpgradeCode has the same value as previous version) I can see in verbose log from MSI that this AUTO_FOUND_DIR exists, the RegistrySearch reads the key value with specific directory and as a result the util:RemoveFolderEx removes that directory and all components that are there located.
My question is: how can I detect why this old property is being used during upgrade and how to get rid of it?
Additional information: the install scope is PerMachine, ALLUSERS is set to 1. The MSI with upgraded version has this property removed.
Without a close look at your complete verbose log to see what's going on, remember that an upgrade does an uninstall of the older installed product. This means that a lot of the logic in the older installed product will happen during your upgrade. So you will definitely see the RegistrySearch running as the older product uninstalls, setting AUTO_FOUND_DIR, and you will see the RemoveFolder that runs during the uninstall.
So it's not clear if you actually have an issue if all you're seeing is the uninstall activity of that older product being uninstalled. That activity is embedded in the installed product.

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) Program files shortcut for per-machine install

Following the example here, I added a shortcut to the ProgramMenuFolder that launches my application. (My code is actually simpler because I don't need the extra folder.)
<DirectoryRef Id='ProgramMenuFolder'>
<Component Id='cmpStartMenuShortcut'
Guid='MY GUID HERE'>
<Shortcut Id='StartMenuShortcut'
Name='$(var.ProductName)'
Icon='MainIcon.ico'
Description='$(var.ProductName)'
Target='[ClientDir]myapp.exe'
WorkingDirectory='ClientDir'/>
<RegistryValue Action='write' Type='integer' Root='HKCU'
Key='Software\Company\Product Name'
Name='installed' Value='1' KeyPath='yes'/>
</Component>
</DirectoryRef>
Since my installation is per machine (ALLUSERS=1, Package/#InstallPrivileges='elevated', and #InstallScope='perMachine') the ProgramMenuFolder is the folder for all users on the machine.
My question has to do with the registry value. My understanding is that it's needed simply to provide a KeyPath for the component that contains the shortcut. The sample uses HKCU, which is a per-user location.
Isn't it a mistake to use a per-user value as a KeyPath for a per-machine component?
If a machine has two admins, and admin #1 installs the product, and admin #2 attempts a repair, then Windows Installer won't see the registry value and think that the shortcut is missing and it will install a duplicate, right?
So I tried changing the RegistryValue/#Root to HKLM, but then WiX complains:
error LGHT0204 : ICE38: Component cmpStartMenuShortcut installs to user profile. It's[sic] KeyPath registry key must fall under HKCU.
error LGHT0204 : ICE43: Component cmpStartMenuShortcut has non-advertised shortcuts. It's[sic] KeyPath registry key should fall under HKCU.
error LGHT0204 : ICE57: Component 'cmpStartMenuShortcut' has both per-user and per-machine data with a per-machine KeyPath.
I don't understand why the key must be under HKCU.
That style of shortcut is for a target that might not be installed now nor at the time it is invoked. It creates the classic .lnk shortcut file. It is useful for shortcuts to targets that your installer is not responsible for but might be useful for users of your product to use (e.g. cmd.exe).
Alternatively, a shortcut for a target you are installing or advertising will be uninstalled when the target is unadvertised (product is uninstalled). For example, WiX installs a shortcut to wix.chm called WiX Documentation. The Shortcut element for an advertised shortcut can be made a child of the File element.
Here is a hand-written example:
<Component Id="ProductComponent">
<File Source="$(var.ConsoleApplication1.TargetPath)" KeyPath="yes">
<Shortcut Id="$(var.ConsoleApplication1.TargetName)Shortcut"
Name="$(var.ConsoleApplication1.TargetName)"
Advertise="yes"
Description="Starts $(var.ConsoleApplication1.TargetName)"
Directory="ProgramMenuFolder" />
</File>
</Component>
To insert the Shortcut element into heat's output, pass it the path to an XSL transform. Snippet:
<xsl:template match="wix:File[contains(#Source,'\myapp.exe')]">
<xsl:copy-of select="." />
<Shortcut Id='StartMenuShortcut'
Advertise="yes"
Name='$(var.ProductName)'
Icon='MainIcon.ico'
Description='$(var.ProductName)'
WorkingDirectory='ClientDir'/>
</xsl:template>
What I would recommend doing is simply making the folder as you said, but not placing the shortcut directly under it. Instead make a shortcut element under the component holding the file. You can use the Directory attribute to specify where you want the shortcut to appear.

Remove registry keys under HKCU on a per machine installation

I build a perMachine installer using WiX 3.6 to install a software I had not developed. Unfortunately the software creates some registry keys under HKCU during execution.
On uninstall, the self created keys should also be removed. It seems not so easy to remove these keys. I am "fighting" with ICE57 and/or ICE38. Both complaining the mix between perUser and perMachine data.
Hopefully you can point me in the right direction on fixing this issue.
To overcome ICEs you should move Per-User registry to separate components and use some registry entry as keyPath for that component, i.e.:
<Component Id='PerUserRegistry' Guid='*'>
<RegistryValue Id="PerUserRegistry_KeyPAth" KeyPath="yes" Root="HKCU" Key="Software\[Manufacturer]\[ProductName]\[ProductCode]\PerUserRegistry" Name="[PackageCode]" Value="[ProductVersion]" Type="string" />
<!--Other Per-user registry goes here-->
</Component>
I completely agree with Christopher: It is common practice to leave per-user data on uninstall, but if removal is necessary, then Active Setup is the only real option.
First I propose you to remove them on Install or Re-Install instead of uninstall, you just need add RemoveRegirty entry and Active Setup, i.e. with this WiX code:
<Component Id='ActiveSetup' Guid='*'>
<RegistryValue Id="ActiveSetup00" Root="HKLM" KeyPath="yes" Key="SOFTWARE\SOFTWARE\Microsoft\Active Setup\Installed Components\[PackageCode]\" Name="StubPath" Value="msiexec /fup [ProductCode] /qb-!" Type="string" />
<RegistryValue Id="ActiveSetup01" Root="HKLM" Key="SOFTWARE\SOFTWARE\Microsoft\Active Setup\Installed Components\[PackageCode]\" Value="[ProductName] [ProductVerion] Configuration" Type="string" />
</Component>
<Component Id='PerUserRegistryCleanup' Guid='*'>
<RegistryValue Id="PerUserRegistry_KeyPath" Root="HKCU" KeyPath="yes" Key="SOFTWARE\SOFTWARE\Microsoft\Active Setup\Installed Components\[PackageCode]\" Name="StubPath" Value="msiexec /fup [ProductCode] /qb-!" Type="string" />
<RemoveRegistryKey Id='PerUserRegCleanup' Root='HKCU' Action='removeOnInstall' Key='Key\To\Be\Removed'/>
</Component>
Note: [PackageCode] use in ActiveSetup is very recommended, so with each new version (build) of MSI package you add separate entry (also see my final note). I used per-user active setup registry as key-path on purpose, so you don't run it for current user twice.
As for removing them after uninstall,
Now, hopefully you need to remove entire key, and not just some values. In either case, I would create custom action to add Registry entry for Active Setup during uninstall (or if there are many such keys/values, create and deploy .CMD file with those and launch it on uninstall, before RemoveFiles action, to add all of them to registry).
Note: that I would strongly recommend adding deleting this registry during install, or you might end up removing per-user values when software is yet installed.
So here's WiX code for all of this:
<CustomAction Id="CA_UninstallRegistryCleanUp" Directory="SystemFolder" ExeCommand="REG.exe ADD "HKLM\SOFTWARE\Microsoft\Active Setup\Installed Components\MySoftName_CleanUp" /v StubPath /d "reg add ^"HKCU\Key\To\Be\Removed^" /va /f" /f" Return="ignore" />
<InstallExecuteSequence>
<Custom Action='CA_UninstallRegistryCleanUp' After='RemoveRegistryValues'>REMOVE~="ALL"</Custom>
</InstallExecuteSequence>
<Component Id='RegCleanup_Remover' Guid='*'>
<RegistryValue Id="PerUserRegistry_KeyPAth" Root="HKLM" KeyPath="yes" Key="SOFTWARE\[Manufacturer]\[ProductName]\[ProductCode]\" Name="DummyKey" Value="[ProductVersion]" Type="string" />
<RemoveRegistryKey Id='RegCleanup_Remover' Root='HKLM' Action='removeOnInstall' Key='SOFTWARE\Microsoft\Active Setup\Installed Components\MySoftName_CleanUp'/>
</Component>
Final notes:
There just two small issues with all this Active Setup stuff: be careful on Windows Terminal Servers; and once active setup was run for one user for current .MSI, it will not run again if you decide to reinstall same package, unless you change its PackageConde or raise version under ActiveSetup registry key. These are topics for another day, let me know if need them clarified.
And don't forget to add all of above Components to some Feature.
The Windows Installer considers this user data and best practice is to not remove it. Either way, it's very difficult to try to remove it anyways since other user profiles are out of scope / context. It's theoretically possible to write a custom action to enumerate profiles and load registry hives but on some versions of Windows ( Vista ) that won't work due to restricted permissions granted to the windows installer service.
If you really, really must be able to remove custom action data on uninstall then take a look at:
Active Setup Explained
You are going to need to leave behind an program (exe for example ) by marking a component as permanent. Then you'll need a custom action to write a registry value during the uninstall (because Windows Installer doesn't support this).
The concept is during the install you lay down an EXE and during the uninstall you leave you. You then write to the ActiveSetup registry key telling it to run your EXE once for each subsequent user to logon to the machine. The EXE then deletes your registry values. Reboot (politely) if needed to unload the extensions from explorer.
But honestly, a better designed application wouldn't need all of this.

Wix: Preventing a file from restoring by Windows Installer Service

We have a little situation here. We are writing an ini file at install-time using following code segment:
<Component Id="_CFG" Guid="{CADE766F-3AF0-40A6-9D35-12AC4FD5B278}" Feature="DefaultFeature" KeyPath="yes" Location="either" NeverOverwrite="yes">
<CreateFolder Directory="CFG" />
<Environment Id="SharedAppend" Name="Path" Value="[CommonFilesFolder]Company Shared\MyDir" Separator=";" Action="set" Part="last" Permanent="yes" System="yes" />
<IniFile Id="MyCFG.ini1" Action="addLine" Directory="CFG" Key="LOCAL_ROOT" Name="ata.ini" Section="ALIAS" Value="[CommonAppDataFolder]Company\MyDir" />
<IniFile Id="MyCFG.ini73" Action="addLine" Directory="CFG" Key="APPLICATIONS" Name="ata.ini" Section="GENERAL" Value="Product1;Product2;Product3;Product4;" />
<RegistryValue Id="Registry47asdf" Root="HKLM" Key="SOFTWARE\Company\MyProd" Name="LocalRoot" Value="[CommonAppDataFolder]Company\MyDir\" Type="string" />
</Component>
This installation is conducted by an Admin user. Now a 2nd user (Standard) modify this file via some application. After that when a 3rd user would logged in and launch the application, Windows Installer Progress Dialog appear and that would restore the file to original one.
I thought, "NeverOverwrite" would prevent this, but it didn’t worked.
I’m assuming that "NeverOverwrite" attribute may not be applicable on element.
Anyone have any idea how to prevent this file from restoring by Windows installer service?
Thanks a bunch..
Modification of an ini file should not trigger Windows Installer Resiliency. What happens is that a component will be reinstalled when its keypath (i.e. a certain file or registry entry) disappears.
So you need to figure out these things:
Which component installs the INI file? (I suppose it is not the component you show in your question, because that one only modifies INI files.)
What is the keypath of that component? (If it is not marked explicitly, wix will take the first file or registry entry in that component.)
Why is the keypath file or registry entry disappearing, thus triggering the reinstallation of that component?
Also, you might want to consider putting the ini file in its own component. This way, it will be its own keypath and it will only be reinstalled by the windows installer resiliency mechanism when it actually disappeared (and not when some other file or registry entry disappears.)