Remove folder in roaming using wix3.10 installer - wix

When uninstalling my application, I'd like to delete a folder that was added by the application.It seems like the uninstaller removes only the directories and files that were originally installed from the MSI file. How do I delete application created folder?

Following the instructions on this link:
use WixUtil extensions:
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension"
>
then read the Folder path previously stored in the registry:
<Property Id="APPLICATIONFOLDER">
<RegistrySearch Key="SOFTWARE\$(var.Manufacturer)\$(var.SkuName)" Root="HKLM" Type="raw" Id="APPLICATIONFOLDER_REGSEARCH" Name="Path" />
</Property>
Make sure the following component is included in you installer scripts:
<DirectoryRef Id="APPLICATIONFOLDER">
<Component Id="CleanupMainApplicationFolder" Guid="*">
<RegistryValue Root="HKLM" Key="SOFTWARE\$(var.Manufacturer)\$(var.SkuName)" Name="Path" Type="string" Value="[APPLICATIONFOLDER]" KeyPath="yes" />
<!-- We need to use APPLICATIONFOLDER variable here or RemoveFolderEx
will not remove on "install". -->
<util:RemoveFolderEx On="uninstall" Property="APPLICATIONFOLDER" />
</Component>
</DirectoryRef>
Use the WiXUtil extension when you call both candle and light: -ext WixUtilExtension

Related

Wix Toolkit 3.11 util namespace fails to load when building

I am trying to build an installer with Wix 3.11.2 in Visual Studio 2019. I have the extension installed and the toolkit.
I want to use the XmlFile tag in the util namespace to modify the app.config of my installed application.
I have added a reference to C:\Program Files (x86)\WiX Toolset v3.11\bin\WixUtilExtension to the installer project.
I have added the util namespace to the Product.wxs
The xmlnamespace is recognized since I get intellisense etc.
However when building I get the error error CNDL0005: The Fragment element contains an unexpected child element 'util:XmlFile'
This is my Product.wxs:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<Product Id="DBDA892E-D414-44E9-9F5E-49DCD25E209B" Name="TestWixError" Language="1033" Version="1.0.0.0" Manufacturer="Test" UpgradeCode="41e16593-71c1-4516-8ec3-bc8f4911e3f1">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate />
<Feature Id="ProductFeature" Title="TestWixErrorSetup" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="TestWixErrorSetup" />
</Directory>
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<Component Id="MainExecutable" Guid="EA05AA88-F299-4EC2-A64A-4ADAB863C4AC">
<File Id="MainExecutable" Source="$(var.TestWixError.TargetPath)"/>
</Component>
<Component Id="MainPdb" Guid="8ACB76F7-BB83-4EA6-9F58-12CD015E88EC">
<File Source="$(var.TestWixError.TargetDir)$(var.TestWixError.TargetName).pdb" />
</Component>
<Component Id="MainConfig" Guid="39BAFF61-BA1A-42C3-9290-67019F6A6257">
<File Source="$(var.TestWixError.TargetPath).config" />
</Component>
</ComponentGroup>
</Fragment>
<Fragment>
<Property Id="HELLO" Value="Hello from install"/>
<util:XmlFile
Id="UpdateHello"
Action="setValue"
File="$(var.TestWixError.TargetPath).config"
SelectionLanguage="XPath"
Permanent="yes"
ElementPath="/configuration/appSettings/add[\[]#key='Hello'[\] ]/#value"
Value="[HELLO]" />
</Fragment>
</Wix>
The visual studio project is just a .net 4.7.1 console app that displays the value from Hello from the appsettings on the console.
The WixUtilExtension.dll is included in the call to candle.exe as you can see in this commandline (some parts redacted):
C:\Program Files (x86)\WiX Toolset v3.11\bin\candle.exe -dDebug -d"DevEnvDir=C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\\" -d"SolutionDir=C:\Users\---\Documents\Projecten\---\TestWixError\\" -dSolutionExt=.sln -dSolutionFileName=TestWixError.sln -dSolutionName=TestWixError -d"SolutionPath=C:\Users\---\Documents\Projecten\---\TestWixError\TestWixError.sln" -dConfiguration=Debug -dOutDir=bin\Debug\ -dPlatform=x86 -d"ProjectDir=C:\Users\---\Documents\Projecten\---\TestWixError\TestWixErrorSetup\\" -dProjectExt=.wixproj -dProjectFileName=TestWixErrorSetup.wixproj -dProjectName=TestWixErrorSetup -d"ProjectPath=C:\Users\---\Documents\Projecten\---\TestWixError\TestWixErrorSetup\TestWixErrorSetup.wixproj" -d"TargetDir=C:\Users\---\Documents\Projecten\---\TestWixError\TestWixErrorSetup\bin\Debug\\" -dTargetExt=.msi -dTargetFileName=TestWixErrorSetup.msi -dTargetName=TestWixErrorSetup -d"TargetPath=C:\Users\---\Documents\Projecten\---\TestWixError\TestWixErrorSetup\bin\Debug\TestWixErrorSetup.msi" -dTestWixError.Configuration=Debug -d"TestWixError.FullConfiguration=Debug|AnyCPU" -dTestWixError.Platform=AnyCPU -d"TestWixError.ProjectDir=C:\Users\---\Documents\Projecten\---\TestWixError\TestWixError\\" -dTestWixError.ProjectExt=.csproj -dTestWixError.ProjectFileName=TestWixError.csproj -dTestWixError.ProjectName=TestWixError -d"TestWixError.ProjectPath=C:\Users\---\Documents\Projecten\---\TestWixError\TestWixError\TestWixError.csproj" -d"TestWixError.TargetDir=C:\Users\---\Documents\Projecten\---\TestWixError\TestWixError\bin\Debug\\" -dTestWixError.TargetExt=.exe -dTestWixError.TargetFileName=TestWixError.exe -dTestWixError.TargetName=TestWixError -d"TestWixError.TargetPath=C:\Users\---\Documents\Projecten\---\TestWixError\TestWixError\bin\Debug\TestWixError.exe" -out obj\Debug\ -arch x86 -ext "C:\Program Files (x86)\WiX Toolset v3.11\bin\\WixUtilExtension.dll" Product.wxs
Things I have tried so far:
Removing the reference, restarting visual studio re-adding the reference
Moving the WixUtilExtension.dll to c:\temp\bin (with all other wix binaries included) and referencing from there.
Moving the WixUtilExtension.dll to the same location as the Product.wxs and referencing from there.
Looking at the WixUtilExtension.dll with ILSpy to verify that it contains a reference to the http://schemas.microsoft.com/wix/UtilExtension namespace (it does in resources it contains an xsd with this namespace)
Any suggestions to get this working are welcome.
Ok,
I am here to answer my own question, hopefully to help someone else in the future.
The util:XmlFile tag needs to be inside a Component tag.
So the fixed fragment looks like this:
<Fragment>
<Property Id="HELLO" Value="Hello from install"/>
<Component Id="TestConfigComponent" Directory="INSTALLFOLDER">
<util:XmlFile
Id="UpdateHello"
Action="setValue"
File="$(var.TestWixError.TargetPath).config"
SelectionLanguage="XPath"
Permanent="yes"
ElementPath="/configuration/appSettings/add[\[]#key='Hello'[\] ]/#value"
Value="[HELLO]" />
</Component>
</Fragment>

Windows Installer not deleting all files on uninstall

I've seen some similar questions asked on here, but none of the solutions given were very clear or worked for me.
I have an installer (created with WiX) which installs certain files and folders. However, when running the installed application, this creates some folders and copies some files into it. These files and folders are not removed on uninstall.
Edited to Show Code so Far:
This INSTALLDIR property:
<Property Id="INSTALLDIR">
<RegistrySearch Id='Registry' Type='raw' Root='HKLM' Key='Software\$(var.Manufacturer)\$(var.ProductName)' Name='Location' />
</Property>
This Component which should set the install location in the registry:
<Component Id="Registry" Guid="*">
<RegistryKey Root="HKMU" Key="Software\$(var.Manufacturer)\$(var.ProductName)">
<RegistryValue Name="Location"
Type="string"
Value="[INSTALLDIR]"
Action="write"
KeyPath="yes" />
</RegistryKey>
<util:RemoveFolderEx On="uninstall" Property="INSTALLDIR" />
</Component>
This does create a record in the registry with the install location, but I'm not sure how to adapt this code to making note of the 'public' directory and removing it - I don't know where the util:RemoveFolderEx should go either (inside which component)
The clearest tutorial I've seen is this one (except that it does have an apparent error).
Replace this block:
<!--
RemoveFolderEx requires that we "remember" the path for uninstall.
Read the path value and set the APPLICATIONFOLDER property with the value.
-->
<Property Id="APPLICATIONFOLDER">
<RegistrySearch Key="SOFTWARE\$(var.Manufacturer)\$(var.SkuName)" Root="HKLM" Type="raw" Id="APPLICATIONFOLDER_REGSEARCH" Name="Path" />
</Property>
with this one:
<!--
RemoveFolderEx requires that we "remember" the path for uninstall.
Read the path value and set the FOLDERTOREMOVE property with the value.
-->
<Property Id="FOLDERTOREMOVE">
<RegistrySearch Key="SOFTWARE\$(var.Manufacturer)\$(var.SkuName)" Root="HKLM" Type="raw" Id="APPLICATIONFOLDER_REGSEARCH" Name="Path" />
</Property>
and this block:
<util:RemoveFolderEx On="uninstall" Property="APPLICATIONFOLDER" />
with this one:
<util:RemoveFolderEx On="uninstall" Property="FOLDERTOREMOVE" />
and you should have a working test.
The reason for using two different properties is given here and here (among with other places).
If you can derive the path from other values you may have set during installation that Windows Installer will preserve for you, such as ARPINSTALLLOCATION, then you can adjust the above implementation to get what you need without having to create your own registry keys.
Builds on B. Murri's answer:
Example: your application installs new files or folders in 'installdir/public'. These files aren't being deleted as they weren't added by the installer.
First, you need to create a registry value which will store where your public directory is installed. This is in case the user changes the install directory.
<!-- Note that the RegistryValue Value is being set to the 'public' directory ID -->
<DirectoryRef Id='INSTALLDIR'>
<Component Id="RemovePublicDir" Guid="your-guid-here">
<RegistryKey Root="HKCU" Key="Software\$(var.Manufacturer)\$(var.ProductName)">
<RegistryValue Name="Location"
Type="string"
Value="[PUBLIC]"
Action="write"
KeyPath="yes" />
</RegistryKey>
<CreateFolder Directory="PUBLIC"/>
<util:RemoveFolderEx Property="FINDPUBLICDIR" On="uninstall"/>
<RemoveFolder Id="PUBLIC" On="uninstall"/>
</Component>
</DirectoryRef>
You now need to add a property which will search for this registry value later on. Make sure your Root value above matches the one below:
<Property Id="FINDPUBLICDIR">
<RegistrySearch Id='Registry' Type='raw' Root='HKCU' Key='Software\$(var.Manufacturer)\$(var.ProductName)' Name='Location' />
</Property>
Add your manufacturer name and product name to variables, like this:
<?define Manufacturer = "My Company"?>
<?define ProductName = "Test Application"?>
Now make sure you call this Component in your Feature tag:
<Feature Id="FeatureId">
<ComponentRef Id="RemovePublicDir" />
</Feature>

WIX- support multiple Instance of Web App on IIS

I am using wix to create my msi installer. the installer will create a new app pool and virtual directory in the IIS to run my web app.
Right now, I am trying to have multiple instance of the web app on the same machine at the same time(either same version, or higher version).
-Is such thing supported in Wix?
-Can I dynamically create multiple app pool and virtual directory on each installation? Because at the moment the name of the appPool, and Virtual directory and destination folder are hard coded inside the Product.WXS file.
I think that you can't even perform setup multiple times, it will just try to uninstall, modify or repair your previous installation or to perform upgrade.
But regarding app pool or website name you can specify it in installation process in your feature selection dialog, to have textbox where you can write down name of website or app pool
<Control Id="WebsiteName" Type="Text" X="..." Y="..." Width="..." Height="..." Text="Website name :" Indirect="no" >
<Condition Action="hide">Installed</Condition>
</Control>
<Control Id="WebsiteName" Type="Edit" X="..." Y="..." Width="..." Height="..." Property="WEBSITENAME" Text="{50}" Indirect="no" >
<Condition Action="hide">Installed</Condition>
</Control>
Than you need to specify your property at Product.wxs
<Property Id="WEBSITENAME" Value="YourDefaultWebSiteName" Secure="yes">
<RegistrySearch Id="FindWebSiteName" Root="HKCU"
Key="SOFTWARE\YourCompany\YourProduct"
Name="WebsiteName" Type="raw"/>
</Property>
<Property Id="APPPOOL" Value="0" Secure="yes">
<RegistrySearch Id="FindWixSetupInstallation" Root="HKCU"
Key="SOFTWARE\YourCompany\YourProduct"
Name="webAppPool" Type="raw"/>
</Property>
Than you need to specify it at your settings where you create app pool and website name
<iis:WebSite Id="DefaultWebSite" Description="Default Web Site">
<iis:WebAddress Id="AllUnassigned" Port="80" />
</iis:WebSite>
<DirectoryRef Id="YourDirectory" >
<Component Id="CMP_CONFIG" Guid="{YOURGUID}" KeyPath="yes">
<iis:WebVirtualDir Id="WebsiteName" Alias="[WEBSITENAME]" Directory="YourDirectory" WebSite="DefaultWebSite">
<iis:WebApplication Id="YourApplicationApp" Name="[WEBSITENAME]" WebAppPool="[WEBSITENAME]" />
<iis:WebDirProperties Id="YourApplicationDirProp" Script="yes" Execute="yes" Read ="yes" DefaultDocuments="Default.aspx"/>
</iis:WebVirtualDir>
</Component>
</DirectoryRef>
And if you want to create shortcut to desktop
<DirectoryRef Id="DesktopFolder">
<Component Id="YourWebsiteAppDesktopShortcut" Guid="{YOURGUID}">
<RegistryValue Root="HKCU"
Key="SOFTWARE\YourCompany\YourProduct"
Name="YourWebsiteApp"
Type="integer"
Value="1"
KeyPath="yes"/>
<util:InternetShortcut Id="WebSiteDesktopShortcut"
Directory="DesktopFolder"
Name="YourWebsiteAppName"
Target="http://localhost/[WEBSITENAME]/"
Type="url" />
</Component>
</DirectoryRef>
And you just need to register all those component reference ID's in your Product.wxs

Bind FileVersion to property in WiX

As for binding the AssemblyVersion of a specific DLL to WiX File Version, is basically easy:
<Product Id="*"
Name="My Application"
Language="1033"
Version="!(bind.FileVersion.MyDll.dll)"
Manufacturer="My Company"
UpgradeCode="E7A0C56F-160B-4C67-9AAC-2FF69630EAF9">
Is there any way that the same can be achieved with Properties in WiX. Like this:
<Property Id="VERSION">
<![CDATA[!(bind.FileVersion.MyDll.dll)]]>
</Property>
I need to update an XML file during installation, with the same version number as the one in the specific DLL. Any suggestions?
You should have a look at the XmlFile Element. It allows you to set values/attributes:
<Component Id="webconfig" Guid="PUT-YOUR-GUID-HERE" NeverOverwrite="yes" Win64="yes">
<File Id="web.config" Source="$(var.bind.ProjectDir)\web.config" KeyPath="yes" />
<util:XmlFile Id="web.config.VersionInfo" File="[#web.config]" Action="setValue" PreserveModifiedDate="yes" ElementPath="//configuration/appSettings/add[\[]#key='VersionInfo'[\]]" Name="value" Value="[VERSION]" Sequence="1" />
</Component>

How to write production version to registry

I want to write the installed version to registry using WiX. Here is the WiX code of mine:
<Product Id="B20795DC-5462-4DE6-B629-8C034D114D3C" Name="ProductName" Language="1033" Version="1.1.3" Manufacturer="Corp" UpgradeCode="8f5c57ff-71fe-4fc6-9400-9bbbb76b4262">
....
<Component Id="ProgramRegistry">
<RegistryKey Id="RegInstallDir" Root="HKLM" Key="Software\[Manufacturer]\[ProductName]" Action="createAndRemoveOnUninstall">
<RegistryValue Type="string" Name="InstallVersion" Value="[Version]"/>
</RegistryKey>
</Component>
It creates a key named "InstallVersion" but the value is empty.
My question is how to write the product version into registry, the expected value should be the same as the version attribute in <Product> tag. (1.1.3)
The ProductVersion property contains the value you're looking for.
You can use ProductVersion property, the way #Stephen suggests. Alternatively, you can use the following approach:
define the version in a WiX variable
reference this variable in both Product element and RegistryValue element
set the product version in your build script and pass it to WiX compiler
Hence, your sample might look like this:
<Product Id="B20795DC-5462-4DE6-B629-8C034D114D3C" Name="ProductName" Language="1033" Version="$(var.Version)" Manufacturer="Corp" UpgradeCode="8f5c57ff-71fe-4fc6-9400-9bbbb76b4262">
....
<Component Id="ProgramRegistry">
<RegistryKey Id="RegInstallDir" Root="HKLM" Key="Software\[Manufacturer]\[ProductName]" Action="createAndRemoveOnUninstall">
<RegistryValue Type="string" Name="InstallVersion" Value="$(var.Version)"/>
</RegistryKey>
</Component>
</Product>
And somewhere in the build script (let's pretend, it is NAnt):
<candle out="${build.fodler}" rebuild="true" extensions="${build.extensions}" exedir="${wxs.dir}">
<defines>
...
<define name="Version" value="1.1.3" />
...
</defines>
<sources basedir="${paths.wxs}">
<include name="**.wxs"/>
</sources>
</candle>