WiX property not found in uninstall log - properties

Property change from the UI disappears from uninstall log.
If I install my app with default property values and then run uninstall the property appears in the uninstall log.
If I change the property value from the UI on uninstall it does not appear in the log.
This is the reason why the apppool and webapp remain in IIS after uninstall, which is not the case with the default values.
<Property Id="WEB_APP_NAME" Value="WebApp" Secure="yes" />
this is how to property looks like.
This is where I add it a value from the UI control
<Control Id="PoolNameEdit"
Type="Edit"
X="100"
Y="45"
Width="160"
Height="17"
Property="WEB_APP_NAME"
Text="{80}"
Indirect="no" />
And this is how I use it
<!-- Define the directory structure -->
<Fragment>
<!--Directory elemens hierarchy always starts with Id="TARGETDIR" Name="SourceDir"-->
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="WEB_APP_FOLDER_LOC" Name="WebInstaller">
<Directory Id="WEBFOLDER" Name ="[WEB_APP_NAME]" />
</Directory>
</Directory>
</Fragment>
<Fragment>
<!--Concatenate user input for folderpath-->
<SetDirectory Id="WEBFOLDER"
Value="[WEB_APP_FOLDER_LOC][WEB_APP_NAME]"
Sequence="both" />
<!--Create new folder-->
<DirectoryRef Id="WEBFOLDER">
<Component Id="cmp_WebDir"
Guid="{E0CE5051-1419-4997-949F-020BC814ECDA}"
KeyPath="yes">
<CreateFolder />
</Component>
</DirectoryRef>
<!--Components-->
<ComponentGroup Id="ProductComponents" Directory="WEBFOLDER">
<!--Client config-->
<Component Id="cmpWebConfig"
Guid="{1C84DF1F-2EA4-46E6-8125-C6FD410AFED9}"
KeyPath="yes">
<Condition>INCLUDECONFIGFILE="1"</Condition>
<File Source="Configuration\Web.config" />
</Component>
<!--Application pool-->
<Component Id="cmpAppPool"
Guid="{00D6ABB1-734F-4788-ADB8-12A30056C513}"
KeyPath="yes">
<iis:WebAppPool Id="MyAppPool"
Name="[WEB_APP_NAME]"
ManagedRuntimeVersion="v4.0"
ManagedPipelineMode="integrated"
Identity="applicationPoolIdentity" />
</Component>
<!--Website-->
<Component Id="cmpMyWebsite"
Guid="{ECD42015-C067-44F3-94D9-5E713BCB586D}"
KeyPath="yes">
<iis:WebSite Id="website_MyWebsite"
Description="[WEB_APP_NAME]"
Directory="WEBFOLDER"
ConfigureIfExists="no">
<iis:WebApplication Id="webapplication_MyWebsite"
Name="[WEB_APP_NAME]"
WebAppPool="MyAppPool" />
<iis:WebAddress Id="webaddress_MyWebsite"
Port="[WEB_APP_PORT]" />
</iis:WebSite>
</Component>
I would have expected after the change of WEB_APP_NAME in the UI, the uninstaller to be able to find it and thus remove appool and webapp from IIS.
Property(S): VirtualMemory = 3353
Property(S): UpgradeCode = {A4F9CA9E-4135-4D6F-AF58-FADA49E265DA}
Property(S): ConfigureIIs7Exec = **********
Property(S): StartIIS7ConfigTransaction = **********
Property(S): RollbackIIS7ConfigTransaction = **********
Property(S): CommitIIS7ConfigTransaction = **********
Property(S): WriteIIS7ConfigChanges = **********
Property(S): NETFRAMEWORK45 = #461808
Property(S): WEBFOLDER= C:\inetpub\WebApp\
Property(S): WEB_APP_FOLDER_LOC = C:\inetpub\
Property(S): WEB_APP_NAME = WebApp
Property(S): WEB_APP_PORT = 8080
Property(S): WEB_APP_USERNAME = ******
Property(S): WEB_APP_DOMAIN_NAME = ******
Property(S): WEB_APP_SQLSERVER_NAME = ******
Property(S): INCLUDECONFIGFILE = 1
this is how the default uninstall log looks like, if I change WEB_APP_NAME to something else, WEB_APP_NAME is not found on the uninstall log where it can be seen above?
Appreciate any ideas that would resolve this!

The key thing to understand here is that Windows Installer does not save Property values. The user entered values (either through UI or through command line arguments) will not be available during repair, upgrade or uninstall. You would imagine, it to be available during uninstall that's a simple ask, but that's the way windows installer works. The most simple solution to bypass this is to read the property and then write it to registry. During repair/uninstall/upgrade do a RegistrySearch and use the value based on what's in registry.
As to why the default value is retained during uninstall, That’s because the initial/default value gets added to the MSI Property table. And that same value gets used from the property table during uninstall as well.

Note: Please prevent the properties from being changeable during uninstall at least. I think you should only accept changes during
fresh install? Or major upgrade? Otherwise the resolved directory name does not match the installed one (same problem you originally had).
Persist Properties: You need to persist the relevant properties when you allow them to be shown and changed in your setup GUI or via command line. Otherwise the properties will be blank when resolved as directory or application names - or whatever capacity you use them for in your setup. Persisting MSI properties is not a built-in Windows Installer feature (just a few system-properties are automatically persisted). Generally an MSI anti-pattern, but that is how it is.
"Remember Pattern" Sample: For regular, PUBLIC properties (UPPERCASE properties) you can use Rob Mensching's remember pattern to save and retrieve property values for repair, modify, uninstall and other maintenance operations. There is a small sample of this property persistence pattern in use here: WIX UI for multiple target directories (Remember Pattern in use).
Installation Modes: There are many installation modes to check when you make a setup: fresh install, repair, modify, self-repair, uninstall, major upgrade uninstall, patching, rollback, resume suspended (reboot and other causes), etc... I would test at least the first 6 types - to make sure resolution works properly.

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 does not remove shortcuts in the INSTALLDIR if not default

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.

WiX: Managed to not overwrite config file during upgrade, however shortcuts are removed

I have a similar problem like forki23 by bring Wix to not overwrite a configuration file during upgrade. I have a config file that should not be overwritten during upgrade, but it should be removed during uninstall. However every solution I find, breaks something else.
If I set NoOverwrite=yes and move the RemoveExistingProducts to InstallFinalize the config file is handled as I wished. However, in this case the shortcut is removed during upgrade for some reason. If I leave RemoveExistingProducts at InstallInitialize, the config file is actually removed during upgrade, however the shortcuts are present.
Why is this happening and is there are way to fix it?
<InstallExecuteSequence>
<RemoveExistingProducts After="InstallInitialize" />
<!-- InstallInitialize causes config-file to disappear during upgrade -->
<!-- InstallFinalize causes shortcuts to disappear during upgrade -->
...
<Property Id="DISABLEADVTSHORTCUTS" Value="1" />
...
<Directory Id="INSTALLLOCATION" Name="MyApp">
<Component Id="MYAPP.EXE" DiskId="1" Guid="...">
<File Id="MYAPP.EXE" Name="MyApp.exe" Source="..." Vital="yes" KeyPath="yes">
<Shortcut Id="startmenuShortcut"
Directory="ProgramMenuDir"
Name="!(loc.ProductName)"
WorkingDirectory='INSTALLLOCATION'
Icon="Icon.ico"
IconIndex="0"
Advertise="yes" />
</File>
<RegistryValue Root="HKLM"
Name="InstallLocation"
Key="$(var.InstallLocationRegistryKey)"
Type="string"
Value="[INSTALLLOCATION]">
</RegistryValue>
</Component>
<Component Id="MYAPP.EXE.CONFIG" DiskId="1" Guid="..." NeverOverwrite="yes">
<File Id="MYAPP.EXE.CONFIG"
Name="MyApp.exe.config"
Source="..."
KeyPath="yes" />
</Component>
...
</Directory>
...
<Directory Id="ProgramMenuFolder">
<Directory Id="ProgramMenuDir" Name="!(loc.ProductPrefix)">
<Component Id="ProgramMenuDir" Guid="...">
<RegistryValue Root="HKCU" Key="SOFTWARE\MyApp"
Type="string" Value="[INSTALLLOCATION]" KeyPath="yes" />
<RemoveFolder Id="ProgramMenuDir" On="uninstall"/>
</Component>
</Directory>
</Directory>
Note A: The config-file is a machine-wide configuration and should apply for all users.
Note B: I'm using WiX 3.7 and the target plattform is Windows 7 and 8.
Theoretically "NoOverwrite=yes and move the RemoveExistingProducts to InstallFinalize" should work, but it s clear we are losing something from the big picture. The best method to see why Windows Installer removes the shortcuts is to create a verbose log when launching the upgrade setup. You can do that in a cmd.exe with this command: msiexec /i [msi path] /L*V debug.log
The post the a download link for the log and the GUIDs of the components hosting the shortcuts so we can see if the log helps us understand what happens.
Windows installer works very exact in those things, and if anything gets removed in the After="InstallFinalize" case, this means, that a component has been removed, MSI has thought is not needed, because not contained in your new version of the msi file. Be very sure the GUID of the component containing MYAPP.exe and the shortcut has not changed in your new version. (Compare with a tool like Orce or Insted). It seems it has!
MSI removes only full components, not only shortcuts. Really! Maybe you have an update problem of shortcuts in Windows. Sometimes such things happen. Try to reboot to be sure, that this happens, what you think. Maybe there is an error in your test procedure (or it's the above mentioned GUID problem). There are not many other possibilities, if you have not custom actions which remove shortcuts or you try to add shortcuts as files or such dangerous stuff.
Putting a shortcut in the same component as the .exe is common, but not optimal in my eyes! I recommend to separate resources as much as possible, so put it in an own component. This has advantages, if you later want to rename the shortcut. Then you can just change the GUID of this component and the important .exe file is not touched.
There is a small disadvantage of that, loosing the direct connection to the file version of MYAPP.exe in overinstall scenarios, so if MYAPP.exe is a shared file between several different setups, this is not recommended. Perfect solutions for this are possible, but are not in the focus here.
Workaround: If you are still able to change the old (first) msi setup, just mark the component MYAPP.EXE.CONFIG as permanent. Then it will not be uninstalled during Major Upgrade (but not uninstalled at all, what has advantages and disadvantages, in other words, it is mostly acceptable for .config files).
If version 1 of your setup is already shipped, then you could do the same with some tricks too.

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.

How do I install to LocalAppData folder?

Following directory setting works perfectly for me.
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id="ProgramFilesFolder">
<Directory Id='INSTALLDIR' Name='MyApp'/>
</Directory>
</Directory>
However, when I tried changing "ProgramFilesFolder" to "LocalAppDataFolder", I got lots of error when using light to link and generate my msi:
D:\runGroup.wxs(53) : error LGHT0204: ICE38: Component cmpA5561BE36D80EB58252E69DDA0C2FF8C installs to user profile. It must use a registry key under HKCU as its KeyPath, not a file.
D:\main.wxs(38) : error LGHT0204 : ICE64: The directory INSTALLDIR is in the user profile but is not listed in the Remove File table.
Looks like "LocalAppDataFolder" is not acceptable for WiX, while I believe it is one of the system folder properties which defined in here.
What am I supposed to use for LocalAppData folder?
I converted an application from being a perMachine install to be a perUser install. In order to properly convert the install I had to add a registry key for each of the components I have.
Originally I had the following:
<Component Id="C.MyExe">
<File Id="Fi.MyExe" Name="$(var.MyExe.TargetFileName)" Source="$(var.MyExe.TargetPath)" DiskId="1">
<Shortcut Id="SC.StartMenu"
Directory="D.ApplicationMenuDir"
Name="$(var.AppName)"
WorkingDirectory="INSTALLDIR"
Icon="MY_ICON.ico"
IconIndex="0"
Advertise="yes"
/>
...
When I moved the exe component to the user install I had to do something like this:
<Directory Id="LocalAppDataFolder" Name="AppData">
<Directory Id="MyAppDirectory" Name="$(var.AppName)">
<Component Id="C.MyExe" Guid="{MY_GUID}">
<CreateFolder />
<RemoveFolder Id="RemoveMyAppDirectory" On="uninstall" />
<RegistryKey Root="HKCU" Key="Software\MyCompany\MyApp">
<RegistryValue Name="MainExe" Value="1" KeyPath="yes" Type="integer" />
</RegistryKey>
<File Id="Fi.MyExe" Name="$(var.MyExe.TargetFileName)"
Source="$(var.MyExe.TargetPath)" DiskId="1" Checksum="yes">
</File>
</Component>
...
The most important part is that you will have to add a registry key which points to HKEY_CURRENT_USER. I added a registry value for each component which indicates that the component is installed.
I also had to remove the following: Advertise="yes".
I had this problem recently. I wanted to convert my installer from per-machine to a per-user but was getting ICE38. I asked on wix-users and one opinion was that you can ignore ICE38 because that was meant as a check for per-machine installs.
See the discussion at wix-users.
Since that is the case, ICE38 is (in my opinion) incorrect and you will want to ignore it. ICE38 implies you are installing per-user resources in the context of a per-machine installation but never verifies that this is so.
Actually authoring a per-user install requires that you ignore ICE38
because it won't ever be accurate for that world.
[Edit]
Looks like you got help here.
From Peter Shirtcliffe:
This is my own, admittedly inexpert, understanding of per-user installations:
Installing to subdirectory of LocalAppDataFolder is perfectly OK in a
per-user MSI. Because of certain scenarios relating to roaming users, you
need to add components containing elements for any
directories you create under LocalAppDataFolder. That's why ICE64 is
appearing.
The ICE38 error is slightly misleading: since you have a per-user
installation, it's safe to ignore as long as the user cannot pick an
alternative installation location that is common to all users. ICE38 is
checking for the situation where multiple users all install the same
component to the same path.
Just posting to help other people (like me).
Ok, just found that we can do it by overwriting "ProgramFilesFolder":
<SetProperty Id="ProgramFilesFolder" Value="[LocalAppDataFolder]" Before="CostFinalize"><![CDATA[NOT Privileged]]></SetProperty>
Another thing to do is, in <Package> we need to set InstallPrivileges to limited.
Well, I can see no reason why "ProgramFilesFolder" can be used directly while "LocalAppDataFolder" can't.
Are you installing per-user or per-machine? Also, what OS versions are you targetting? You might want to read:
Authoring a single package for Per-User or Per-Machine Installation context in Windows 7