I am struggling against the issue with wix toolset: why after I uninstall an application some folders including the "logs" are not deleted? is it a bug or not?
<Directory Id="logs" Name="logs">
<Component Id="logs" Guid="0A4D0A3F-2E0D-1B1A-1C6D-1A0F8FAAABC6" Win64="$(var.is64)">
<CreateFolder Directory="logs">
<Permission GenericAll="yes" User="Everyone" />
</CreateFolder>
<RemoveFolder Id="logs" On="uninstall"></RemoveFolder>
</Component>
</Directory>
Sometimes if the application you install generates files or folders after the installation, that can prevent WiX from removing the parent folder during uninstall.
If there are log files created after install, you can purge them by adding this to your existing component:
<RemoveFile Id="RemoveLogFiles" Name="*.*" On="uninstall" />
If your application also creates subdirectories and RemoveFile doesn't get rid of them, I would look into using RemoveFolderEx(http://wixtoolset.org/documentation/manual/v3/xsd/util/removefolderex.html). This would require you to to create a Property and write the directory path to a place in the registry so you can set the Property before RemoveFolderEx runs. You can't just use the Directory Id because RemoveFileEx runs before the MSI creates the Directory properties. Read the link I provided if my explanation didn't make sense to you.
Hope this helps!
Related
Hi there I am having a problem getting my Wix installer to remove elements on Uninstall. The problem folders and files are located on our corporate specified programdata folder 'D:\programdata'. The folders get created OK, however will not remove on Uninstall. The folder structure is as follows
D:\programdata
Company Name
App Name
Logs
QueryOutput
The following is an excerpt from the relevant section of the product.wxs file:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="CommonAppDataFolder" Name="CommonAppData" >
<Directory Id="dirCompanyAppData" Name="Company Name">
<Directory Id="dirAppNameAppData" Name="AppName">
<Component Id="cmpDirCommonAppData" Guid="{F808944A-D898-43F3-BA1D-A35A3FD7DF41}" KeyPath="yes">
<CreateFolder Directory="dirAppNameAppData" />
<RemoveFile Id="PurgeAppName" Name="*.*" On="uninstall" />
<RemoveFolder Id="idDirAppNameAppData" On="uninstall" Directory="dirAppNameAppData" />
</Component>
</Directory>
<Component Id="cmpDirCompanyName" Guid="{A1E7E75A-D582-40C5-BD6B-D36BFB11795E}" KeyPath="yes">
<RemoveFile Id="PurgeCompanyName" Name="*.*" On="uninstall" />
<RemoveFolder Id="idDirCompanName" On="uninstall" Directory="dirCompanyNameAppData" />
</Component>
</Directory>
</Directory>
<Directory Id="ProgramFilesFolder">
... etc
Note company and application identifying elements have been replaced in the code. I have left out the remainder of the wxs file for brevity and because I believe the relevent code is included in this extract. Any assistance much appreciated, this has me stumped.
Kind Regards
Paul J.
From RemoveFolder definition:
Remove an empty folder if the parent component is selected for installation or removal.
In your case the AppData folder probably has user specific configuration in it like it is supposed to.
I think all the component planning is done first, then executed. So, RemoveFile will plan all the files in that folder to be removed and RemoveFolder will decide it shouldn't delete the folder because at the time of planning, the folder still has stuff in it that is not part of the installation included components and therefore not empty.
You will need to use util:RemoveFolderEx. Again there is another caveat to using this.
Because it might dramatically affect Windows Installer's File Costing, the temporary rows must be written before the CostInitialize standard action. Unfortunately, MSI doesn't create properties for the Directory hierarchy in your package until later, in the CostFinalize action.
So you need to manually set a directory based off of a property you probably read from the registry before the WixRemoveFoldersEx action which I think is scheduled just before CostInitialize.
Using WiX, a file can be installed to ProgramFiles using the following:
<DirectoryRef Id="ApplicationBinDirectory">
<Component ...>
<File Id="..." KeyPath="yes" Source="..." />
</Component>
</DirectoryRef>
Now I want to do the same, but the target is a folder in local application data. The file really belongs there according to http://msdn.microsoft.com/en-us/library/windows/apps/hh464917.aspx
<!-- this is the folder in %LOCALAPPDATA% -->
<Directory Id="LocalAppDataFolder">
<Directory Id="ApplicationLocalAppDataDirectory" Name="my folder name"/>
</Directory>
<DirectoryRef Id="ApplicationLocalAppDataDirectory">
<File Id="fil" KeyPath="yes" Source="..." />
</DirectoryRef>
But I get the following error and warning now:
installer.wxs(63) : error LGHT0204 : ICE38: Component ApplicationConfiguration installs to user profile. It must use a registry key under HKCU as its KeyPath, not a file.
installer.wxs(64) : warning LGHT1076 : ICE91: The file 'fil' will be installed to the per user directory 'ApplicationLocalAppDataDirectory' that doesn't vary based on ALLUSERS value. This file won't be copied to each user's profile even if a per
machine installation is desired.
Adding RegistryValue and RemoveFolder as I have to do it for start menu shortcuts doesn't change the message.
I'm kind of lost here. How do I install a file to local appdata? Please note that I want to install only 1 file there, in addition to the usual setup in program files.
This should help. It shows adding a registry key item to be the keypath:
http://nofoe.blogspot.com/2008/12/wix-it-must-use-registry-key-under-hkcu.html
We're using WiX to bundle up our ASP.NET code into an MSI installer, and we need to set the [ComputerName]\IIS_WPG group to have modify permissions on a folder (named "CO") under the installation directory. The directory structure looks like this after install
C:\inetpub\wwwroot\MyApp\CO
The CO folder is actually part of the .NET solution file, and when the application is bundled into an MSI, the CO folder is included automatically because there are XML files underneath it which are marked as "Content" (this means that I don't need to explicitly use CreateFolder to create the CO folder underneath MyApp). These XML files are updated by the application, which is why the IIS_WPG needs modify permission.
My question is, how do I set the permission on the CO folder? I thought that I might be able to create the folder in an attempt to overwrite whatever permissions are included by default but it doesn't set the permission - I'm guessing that's because the folder I'm creating is overwritten by the actual folder in the MSI, thereby overwriting the permissions that I've set.
I thought that I might need to create a CustomAction which gets executed at some point before InstallFinalize, but I'm lost because I don't understand how to link the folder creation to the CustomAction. I've tried this
<InstallExecuteSequence>
<Custom Action="SetPermission" Before="InstallFinalize" />
</InstallExecuteSequence>
<CustomAction Id="SetPermission" Directory="CODIRECTORY">
<Directory Id="CODIRECTORY" Name="CO" LongName="CO">
<Component Id="CODIR" Guid="D28C9DA4-D20F-45E6-9C9B-4687177EDF41" DiskId="1">
<CreateFolder>
<Permission GenericAll="yes" User="[ComputerName]\IIS_WPG" />
<Permission GenericAll="yes" User="Administrators" />
<Permission GenericRead="yes" GenericWrite="yes" User="ASPNET" />
</CreateFolder>
</Component>
</Directory>
</CustomAction>
But that gives me this error
error CNDL0049 : The CustomAction element's DllEntry, Error, ExeCommand, JScriptCall, Script, Value, or VBScriptCall attribute was not found; one of these is required.
error CNDL0005 : The CustomAction element contains an unexpected child element 'Directory'
I've also tried to use PermissionEx, but I get this error when using that
error CNDL0005 : The CreateFolder element contains an unexpected child element 'util:PermissionEx'.
even though I added xmlns:util="http://schemas.microsoft.com/wix/UtilExtension" at the top of the file.
How do I get the CustomAction to work and Folder Permissions correct?
Your settings looks quite similar to mine, there are little differences.
We do not set the permissions in a custom action, but with creating the directory.
This one works for us:
<Directory Name="SourceDir" Id="TARGETDIR">
<Directory Id="CommonAppDataFolder">
<Directory Name="$(var.InstallCompany)" Id="CompanyDir">
<Directory Name="$(var.InstallProduct)" Id="ProductDir">
<Directory Name="$(var.InstallFolder)" Id="INSTALLLOCATION">
<Component Id="Permission.InstallFolder" Guid="{7C5234ED-EE92-468A-A765-27E5747705DB}">
<CreateFolder>
<Permission ChangePermission="yes" GenericAll="yes" User="Administrators"/>
<Permission User="Everyone" WriteExtendedAttributes="yes" WriteAttributes="yes" CreateFile="yes" CreateChild="yes" GenericWrite="no" GenericRead="yes" GenericExecute="yes"/>
</CreateFolder>
</Component>
</Directory>
</Directory>
</Directory>
</Directory>
</Directory>
I had to create a CustomAction, similar to what is done in this post
Set Permission Using cacls
I know it's not ideal, but it solves the problem. We could potentially upgrade to Wix 3 and use PermissionEx, but that's a battle I'll have to fight another day.
Not sure it helps, but I did some more investigation and found that we're using Wix 2, so I'm guessing that's why we couldn't use PermissionEx.
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
My application has a settings file which I need to keep when user uninstalls the app. Can I do this using components, or do I need to use custom actions?
This is what I have so far (not working):
<Directory Id="INSTALLLOCATION" Name="_MyApp">
<Component>
<File KeyPath="yes" Source="$(var.Source)\settings.ini" />
</Component>
<Component Id="Backup" Guid="SOME-GUID">
<Condition>REMOVE=ALL</Condition>
<CopyFile Id="settings.ini" Delete="no" SourceProperty="INSTALLLOCATION" DestinationProperty="INSTALLLOCATION" SourceName="settings.ini" DestinationName="settings.ini.bak" />
</Component>
</Directory>
If it matters, these components belong to:
<Feature Id="Default" Level="1">
<ComponentRef Id="settings.ini" />
<ComponentRef Id="Backup" />
</Feature>
I suspected this does not work because action MoveFiles runs after RemoveFiles and then there's nothing left to move, so I removed settings.ini from installer and copied it manually after installation. I was thinking this way the ini file is still there after RemoveFiles and it will be renamed. Well, the ini file is there indeed but it doesn't get renamed. Any idea why?
Windows Installer doesn't have a built-in mechanism for backing up and restoring files. Usually the solution is to use a custom action which copies the file.
I removed settings.ini from installer
and copied it manually after
installation. I was thinking this way
the ini file is still there after
RemoveFiles and it will be backed-up.
Well, the ini file is there indeed but
it doesn't get backed-up.
Try creating an uninstall log and see what happens when MoveFiles is executed. As a side note, I don't see how copying the file manually after install is better than a backup custom action.