Per-Machine install problem with User Profile - wix

When building my WXS data into an MSI I get the following error:
ICE38: Component CreateFolder installs to user profile. It must use a registry key under HKCU as its KeyPath, not a file.
This is confusing me cause I have my project set to be a per-machine installation, so from my understanding it should install to the "C:\Users\All Users" or "C:\Users\Default" not to the actual user profile. I have tried a couple of different methods to say it is a per-machine installation, but none of them work. Any thoughts would be greatly appreciated. I am stumped!
To make it an per-machine I tried these two settings (separately) and neither one worked.
<Property Id="ALLUSERS" Value="2" />
and
<Package InstallScope="perMachine" ... />
EDIT: Code for CreateFolder
<Directory Id="AdminToolsFolder" SourceName="Admin Tools">
<Component Id="CreateFolder" Guid="{452A617E-XXXX-XXXX-XXXX-3710802B3BBD}" KeyPath="yes">
<CreateFolder Directory="AdminToolsFolder" />
</Component>
</Directory>

I wrote up a solution to this problem a while ago: http://robmensching.com/blog/posts/2007/4/27/How-to-create-an-uninstall-shortcut-and-pass-all-the.

If you want to create a shortcut you can use the Shortcut element:
<Directory Id="AdminToolsFolder" SourceName="Admin Tools">
<Component Id="MyShortcuts" Guid="<guid value>">
<Shortcut Id="Shortcut_MyAdminTool" Directory="AdminToolsFolder"
Name="My Admin Tool" Target="[#AdminTool]"
Show="normal" WorkingDirectory="TARGETDIR" />
</Component>
</Directory>

Related

Issues with uninstall program in control panel built with WiX toolset

I'm creating msi installer that run services with auto start for windows using wix toolset. msi was successfully compiled and installed, and services are working correctly.
The problem is that, when i try to uninstall the program in control panel, it's showing the following message:
I tried to remove the program from regedit and tried to install using the command:
MsiExec /I installer.msi REINSTALLMODE=voums REINSTALL=ALL
Here is the code of product and package declarations:
<Product Name='Foobar 1.0' Manufacturer='Acme Ltd.' Id='6DA5C23A-86C7-4D14-AEC0-86416A69ABDE' UpgradeCode='6DA5C23A-7349-453F-94F6-BCB5110BA4FD' Language='1033' Codepage='1252' Version='1.0.0'>
<Package Id='*' Keywords='Installer' Description="Acme's Foobar 1.0 Installer" Comments='Foobar is a registered trademark of Acme Ltd.' Manufacturer='Acme Ltd.' InstallerVersion='100' Languages='1033' Compressed='yes' SummaryCodepage='1252' />
In INSTALLDIR, i have an exe file for service, and do some actions:
<Directory Id='INSTALLDIR' Name='Foobar 1.0'>
<Component Id='ConfPathEnv' Guid='6DA5C23A-6BE3-460D-A14F-75658D16550B' KeyPath="yes">
<Environment Id="AQLIGHT_CONFIG_PATH" Name="AQLIGHT_CONFIG_PATH" Value="[INSTALLDIR]config.json" Permanent="yes" Part="last" Action="set" System="yes" />
</Component>
<Component Id='MainExecutable' Guid='6DA5C23A-83F1-4F22-985B-FDB3C8ABD471'>
<File Id='serviceEXE' Name='service.exe' DiskId='1' Source='service.exe' KeyPath='yes' />
<ServiceInstall Id="InstallService" Name="AqLightService" DisplayName="AqLightService 1.0" Start="auto" ErrorControl="normal" Arguments="install" Type="ownProcess" />
<ServiceControl Id="ControlService" Name="AqLightService" Start="install" Stop="uninstall" Remove="uninstall" Wait="yes" />
</Component>
</Directory>
In order to remove folder when uninstalling the program, i use the following code:
<Directory Id="ProgramMenuFolder" Name="Programs">
<Directory Id="ProgramMenuDir" Name="Foobar 1.0">
<Component Id="ProgramMenuDir" Guid="6DA5C23A-7E98-44CE-B049-C477CC0A2B00">
<RemoveFolder Id='ProgramMenuDir' On='uninstall' />
<RegistryValue Root='HKCU' Key='Software\[Manufacturer]\[ProductName]' Type='string' Value='' KeyPath='yes' />
</Component>
</Directory>
</Directory>
I tried to change the GUIDs for each componet several times, but it did't help.
Custom Action: That error message can mean a number of things, but most commonly it is caused by a failing custom action. However, in this case it looks like the message is from iTunes?
Logging: No two ways about it: you need a verbose log file to try and make sense of this (adjust paths as appropriate):
msiexec.exe /x "Setup.msi" /L*V "C:\Setup.log"
Self-Repair?: My guess is that you have a corrupted machine / Windows Installer Database, or a self-repair problem that you have
turned into a corrupt machine by hacking the registry. Maybe. On Self-Repair.
Conditioning: When you need conditions to run only at certain times / installation modes - you need to deal with conditioning. There are many previous answers on MSI conditions. They are always hard to get right and testing is essential. Please see the answers below for information:
"MSI Condition Cheat Sheet"
Wix custom uninstallation action - how to run before msi removing files
How to execute conditional custom action on install and modify only? - tip on how to test conditions using VBScript and the MSI API call "EvaluateCondition" at runtime - ze proof izt in ze pudding! :-).
This condition might be enough for you (no guarantees):
NOT Installed AND NOT UPGRADINGPRODUCTCODE
Please test in all installation modes: : install, uninstall, modify, repair, self-repair, patching, major upgrade, etc.... Hard to tell how things can conspire, no substitute for real-world testing (just to state the obvious).
Here are more details on logging and interpreting the log file:
Installsite.org on logging
Interpreting MSI log files, etc...
Event logging, etc...
And another answer on logging.

How do I remove files and Folders from ProgramData Folder on Uninstall

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.

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.

WiX3 - util:XmlFile element executes again when new user first uses a per-machine installation

i created WiX installer project for deploying my .net winform app on a customer machine. The app only scans documents and saves the images to database on a server. The scanner is quite specific and only one in the company, but there are approx. four users that can occasionaly use it => app will be installed just on a single workstation dedicated only for scanning - most of the time it will be free and any of these users can come, scan the docs and go continuing his work.
=> i am doing a per-machine installation: ALLUSERS is hardcoded to 1.
Because the database servers in production are not controlled by me and i do not really know, where the database will be stored, i can not pack correct ConnectionStrings.config file to the MSI archive. Instead of it the setup modifies this config according to parameter values provided by the user during install. For updating the connection strings I use util:XmlFile element. The connectionstrings.config file is stored in installation directory together with app binaries.
Everything seemed to work fine, until I simulated two users using this per-machine installation. I have executed my wix setup project under my own account, the XML config file had been correctly updated and then I launched the application and tested the connection strings are ok. Everything was fine.
Then I switched to another user account. The shortcut was already present in the program menu - just as I would have expected since the installation is per-machine. So I clicked the shortcut and then (unexpectedly for me) a progress bar window "Wait until the configuration of product XY is finished." appeared. (Note that my machine locale is not english, the message would probably be slightly different on an english locale workstation). After few seconds the window disappeared and my application launched. Unfortunately it was not able to connect to the database, since the connectionStrings.config file has been rewritten - the connection strings have been updated using default (=incorrect) property values.
I have been investigating why the setup launchs again whenever new user-account tries to use it. It is because of the shortcut element (Shortcut is placed to 'ProgramMenuFolder'. There is a request for uninstall action, which AFAIK requires a parent Component and this Component needs a KeyPath, which has to be a registry key under HKCU.). When I remove all the Program-Menu-Shortcut-stuff from WXS, MSI is not launched again after switching user context.
Result is that I have setup program, which is able to configure connection to a database according to input parameters. But any later attempt to use the app from a second user-account just sends this configuration down the toilette. In production environment this would mean, that an administrator has to come and manually change the connection strings every time new user tries to use the app, which is of course unacceptable behavior.
This is simplified version of my WiX source:
<?define ProductID = "11111111-1111-1111-1111-111111111111" ?>
<?define ProductName = "MyProduct" ?>
<?define ProductLocalName = "MyLocalLanguageProductName" ?>
<!-- application's root registry path, where it stores its settings -->
<?define ApplicationRootRegistryKey = "Software\MyCompany\MyProject\MyBuildConfiguration" ?>
<Product Id="$(var.ProductID)" UpgradeCode="{11111111-1111-1111-1111-111111111112}"
Name="$(var.ProductName)" Version="1.10.1103"
Manufacturer="MyCompany"Language="1029" Codepage="1250">
<Package Id="*" InstallerVersion="200" Compressed="yes"
Description="$(var.ProductName) Installer" Languages="1029"
SummaryCodepage="1250" />
<Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />
<!-- always install the app for all users -->
<Property Id="ALLUSERS" Value="1"/>
<!-- initialize properties used for adjusting connection strings.
The user will provide valid property values through command-line -->
<Property Id="DB_SERVER_NAME" Value="please-specify-db-server-name"/>
<Property Id="DB_NAME" Value="please-specify-db-name"/>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder" Name="PFiles">
<Directory Id="CompanyProgramFilesFolder" Name="CompanyName" >
<Directory Id="INSTALLDIR" Name="ProjectName">
<Directory Id="InstallDirApp" Name="Bin" />
</Directory>
</Directory>
</Directory>
<Directory Id="ProgramMenuFolder" Name="Programs">
<Directory Id="AppProgramMenuDir" Name="$(var.ProductLocalName)">
<Component Id="ProgramMenuDir" Guid="*">
<RemoveFolder Id='AppProgramMenuDir' On='uninstall'/>
<RegistryValue Root='HKCU' Key='$(var.ApplicationRootRegistryKey)' Type='string' Value='' KeyPath='yes' />
</Component>
</Directory>
</Directory>
</Directory>
<DirectoryRef Id="InstallDirApp">
<Component Id="Executable" Guid="*">
<File KeyPath="yes" Source="$(var.MyProject.TargetPath)">
<Shortcut Id="ProgramMenuShortcut" Name="$(var.ProductLocalName)"
Directory="AppProgramMenuDir" Advertise="yes"
WorkingDirectory="InstallDirApp" Icon="AppIcon.ico" IconIndex="0"/>
</File>
</Component>
<!-- ConnectionStrings config file deployment and settings adjustment -->
<Component Id="ConnectionStrings.config" Guid="*">
<File KeyPath="yes" Source="$(var.Csob.ChequesScanning.SmartShell.TargetDir)ConnectionStrings.config" />
<!--</Component>
<Component Id="xml01" Guid="*">-->
<!--<Condition><![CDATA[NOT Installed]]></Condition>-->
<!-- this sets the connection strings according to provided parameters -->
<util:XmlFile Id="SetConnectionString" Action="bulkSetValue"
File="[#ConnectionStrings.config]"
ElementPath="//add" Name="connectionString"
Value="Data Source=[DB_SERVER_NAME];Initial Catalog=[DB_NAME];Integrated Security=True;Pooling=True"
Permanent="yes" />
</Component>
</DirectoryRef>
<Icon Id="AppIcon.ico" SourceFile="$(var.MyProject.ProjectDir)Resources\AppIcon.ico" />
<Feature Id="ProductFeature" Title="MyProjectName" Level="1">
<ComponentRef Id="Executable" />
<ComponentRef Id="ConnectionStrings.config"/>
<ComponentRef Id="ProgramMenuDir" />
</Feature>
</Product>
</Wix>
I have tried these steps to resolve the problem, but nothing helped me:
1) I have separated the and the to independent components.
2) I Have Tried adding a NOT INstalled under these components.
3) I have tried writing a registry value to HKLM during installation. I have added a RegistrySearch and Property for that registry value and then used that value as a condition (in fact just a replacement of "NOT Installed" from the previous)
Can anyone help with this? What am I doing wrong?
Thanks in advice
Marek
Root your registry key under HKMU (See reference). This will correctly root your registry key in either HKLM or HKCU depending on the value of the ALLUSERS property.

Directory NN is in the user profile, but is not listed in the RemoveFile table

When I am trying to create the installer i am getting the following error:
The directory ProgramMenuDir is in the user profile but is not listed in the RemoveFile table.
How do I resolve this issue? Below is the directory structure I am using:
<Directory Id="ProgramMenuFolder" Name="Programs">
<Directory Id="ProgramMenuDir" Name="E">
<Directory Id="Monarch" Name="Monarch">
<Component Id="Monarch" Guid="*">
<RemoveFolder Id='Monarch' On='uninstall' />
<RemoveFolder Id='ProgramMenuDir' On='uninstall' />
<RegistryValue Root='HKCU' Key='Software\[Manufacturer]\[ProductName]'
Type='string' Value='' KeyPath='yes' />
</Component>
</Directory>
</Directory>
</Directory>
As far as I can see, the problem is in the usage of <RemoveFolder/> element. If the 'Directory' attribute is omitted, it takes the directory of the parent component. In your case, it is a directory with Id="EFIMonarch". This explains why you get the warning for the outer directory (ProgramMenuDir), but don't get it for EFIMonarch directory.
Try replacing:
<RemoveFolder Id='ProgramMenuDir' On='uninstall' />
with
<RemoveFolder Id='RemoveProgramMenuDir' Directory='ProgramMenuDir' On='uninstall' />
Also, it is a good idea to be explicit for every RemoveFolder element.
Hope this helps.
If other answers are still not working for you, try to check Suppress ICE validation option, Visual studio will ignore these validations, just follow this route:
YourProject -> Properties -> Tool Settings
Just writing this up for some others who may still experience this problem even after following the answer for this question.
I had the same problem, and even after explicitly specifying the Directory in the RemoveFolder did not help me, I tried to put this DirectoryRef containing the shortcut install/uninstall somponents right after the TARGETDIR within the same fragment and it helped fix my issue.
If following answer still still not working. Try to reboot your visual studio.