I have a default directory for installation:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFiles64Folder">
<Directory Id="INSTALLFOLDER" Name="$(var.Manufacturer) $(var.ProductName)"></Directory>
</Directory>
</Directory>
During installation, I allow users to change directory. If a user does change directory during major upgrade, do I have to retrieve the directory manually and set INSTALLFOLDER with actual path or is there a way to detect it automatically somehow?
This is not supported by Windows Installer directly; I think you're looking for the "remember property" pattern. The strategy is:
On initial installation, save the value of INSTALLFOLDER to the registry in a well-known location.
When starting an upgrade, retrieve the value from the registry and use it.
The authoring looks like this:
<!-- Retrieve the property from the registry during AppSearch -->
<Property Id='REMEMBERME'>
<RegistrySearch Id='RememberProperty'
Root='HKCU'
Key='SOFTWARE\My Company\My App'
Name='Remembered'
Type='raw' />
</Property>
<!-- Save the value in the registry for future upgrades -->
<Component Directory='INSTALLFOLDER'>
<RegistryValue Root='HKCU'
Key='SOFTWARE\My Company\My App'
Name='Remembered'
Value='[REMEMBERME]'
Type='string' />
</Component>
Rob Mensching's blog post describes this in much more detail.
Related
I am struggling with Wix installer. During the install, the INSTALLDIR is overridden to a custom folder (not in Program Files). When I uninstall, the INSTALLDIR and the TARGETDIR gets set to E:\ no matter what I do.
How do I fix this? Here are the relevant snippets:
<Property Id="ARPINSTALLLOCATION">
<RegistrySearch Id="GetINSTALLDIRFromRegistry" Root="HKLM"
Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductCode]" Name="InstallLocation" Type="raw" />
</Property>
<SetDirectory Id="TARGETDIR" Value="[ARPINSTALLLOCATION]">Installed</SetDirectory>
<SetProperty Id="ARPINSTALLLOCATION" Value="[INSTALLDIR]" After="CreateFolders" Sequence="execute">NOT Installed</SetProperty>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="INSTALLDIR" Name="WinApp">
</Directory>
</Directory>
</Fragment>
Thanks in advance. I am using Wix Tools 3.10.
Update 1
Here are my findings (for now. please correct me as I am still new to Wix)
Contrary to popular belief, the installer does not seem to set the install folders automatically during uninstall.
An important exception to the above rule is when the install folders are "well known" folders like ProgramFilesFolder.
So, If you set your directory structure to this:
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="INSTALLDIR">
<Directory Id="TEST" Name="WinApp" />
</Directory>
</Directory>
</Fragment>
You must set the INSTALLDIR directory through a property assignment or through a custom action. For example:
<Property Id="INSTALLDIR"><![CDATA[C:\mydir1\mydir1]]></Property>
If you set INSTALLDIR dynamically through a dialog box, you must read it from the registry or from a file (where you stored it during the install).
Update 2
Slightly improved version
<SetProperty Id="ARPINSTALLLOCATION" Value="[INSTALLDIR]" Sequence="execute" After="InstallFiles" />
<Property Id="INSTALLDIR" Secure="yes">
<RegistrySearch Id="Locate_EXISTINGINSTALLDIR" Root="HKLM" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductCode]" Name="InstallLocation" Type="directory" />
</Property>
<CustomAction Id="SetTARGETDIR"
Directory="TARGETDIR"
Value="[INSTALLDIR]"
Return="check" />
<InstallExecuteSequence>
<!--this action launches a window to allow the user to choose the folder. don't want
to use standard MSI/Wix dialogs as the functionality is limited
-->
<Custom Action='SelectFolderCustomAction' Before='CreateFolders'>NOT Installed</Custom>
<!--<Custom Action='RegistrationInfoCustomAction' Before='InstallFinalize'>NOT Installed</Custom>-->
<!--<Custom Action='UninstallCustomAction' Before='RemoveFiles'>Installed AND REMOVE = "ALL"</Custom>-->
<Custom Action="SetTARGETDIR" Before="RemoveFiles">Installed AND REMOVE = "ALL"</Custom>
</InstallExecuteSequence>
I dont understand what you are trying to do above. Why are you reading the install location from the registry?
This is what I do for installing to "D:\Program Files" folder.
<Property Id="ROOTDRIVE"><![CDATA[D:\]]></Property>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFiles" Name="Program Files">
Remove the SetDirectory element, it's obsolete. When the product is already installed, Windows Installer automatically sets the directory properties to the pathes of the actual installation directories.
The RegistrySearch for ARPINSTALLLOCATION looks strange too. Normally this property should only be set during first install, which you already do by using the SetProperty element.
So the following should be enough:
<SetProperty Id="ARPINSTALLLOCATION" Value="[INSTALLDIR]" After="CreateFolders" Sequence="execute">NOT Installed</SetProperty>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="INSTALLDIR" Name="WinApp"/>
</Directory>
</Fragment>
I am using the feature tree dialog set which allows for a customised install directory, I am then trying to store this install directory inside a registry
As per Wix's documentation I am defaulting the ui variable to the directory I want
<Property Id="WIXUI_INSTALLDIR" Value="VERSION" />
<SetProperty Id="VERSION" Value="[ProgramFilesFolder]Thermo\SampleManager\[PROP_VERSION]" After="CostInitialize" />
Then trying to set the registry as follow
<RegistryKey Root="HKCR" Key="x\x\x">
<RegistryValue Type="string" Value="[VERSION]aaasx\aasx" />
</RegistryKey>
Obviously the [VERSION] does not change with the UI selection, which variable should I be using to change the configured directory? Feel as if I've tried every suggested [INSTALLDIR] etc.
Directory is configured as such
<Directory Id="ProgramFilesFolder" Name="PFiles">
<Directory Id="COMPANY" Name="Company">
<Directory Id="PRODUCT" Name="Product">
<Directory Id="VERSION" Name="Version">
Further investigation found that using file references ( [#file] ) works, which solves 90% of my problems, however pointing/storing a directory path does not work
You can use only RegistryValue like that:
<RegistryValue Root='HKCR' Key='SOFTWARE\MyProcudt'
Name='MyPath' Value='[INSTALLDIR]'
Type='string' />
It will create registry in HKCR/software/myproduct name "mypath" with value [installdir] (set ealrier).
I'm creating a program which is being installed by Wix, using VS 2010 and I've already got the product.wxs ready.
In my wxs file, I've got directory definitions which looks like this:
<SetDirectory Id="INSTALLFOLDER" Value="[WindowsVolume]Myapp" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="INSTALLFOLDER" Name="Myapp">
<Directory Id="Myapp_Installer_Dir" Name="Myapp">
<Directory Id="BIN" Name="Bin" />
<Directory Id="ICONS" Name="Icons" />
</Directory>
</Directory>
</Directory>
And then I got these file installation definitions:
<DirectoryRef Id="Myapp_Installer_Dir">
<Component Id="INSTALLER_Myapp" Guid="{94F18477-8562-4004-BC6F-5629CC19E4CB}" >
<File Source="$(var.Myapp.TargetPath)" KeyPath="yes"/>
</Component>
</DirectoryRef>
<DirectoryRef Id="BIN">
<Component Id="INSTALLER_Data" Guid="{545FB5DD-8A52-44D7-898E-7316E70A93F5}" >
<File Source="$(var.Data.TargetPath)" KeyPath="yes"/>
</Component>
...
And it continues in that manner. The files for the "ICONS" directory are defined as well.
I am also using the WixUI_InstallDir dialog set and I got these lines present as well:
<Property Id="WIXUI_INSTALLDIR" Value="Myapp_Installer_Dir" />
<UIRef Id="WixUI_InstallDir" />
The problem is when the user installs the program and changes the value of the installation folder, the files of the "Bin" and "Icons" are installed to their correct path, but the Myapp target is installed to a fix location which was defined at the start as the default installation path.
Why do only the bin and icon files installed to the correct folder the user wanted, but the myapp target does not?
I have finally figured out the problem.
After searching for a while, I came across this document:
WixUI_InstallDir Dialog Set
The relevant part: "The directory ID must be all uppercase characters because it must be passed from the UI to the execute sequence to take effect."
And as you can see in my code: "Myapp_Installer_Dir" does not meet this criteria.
After changing it to "MYAPPINSTALLERDIR", everything worked.
I'm not quite sure, but this is what I think has happened.
When you author a SetDirectory element, you basically add a custom action which sets a directory to the MSI database. As long as you do not specify the sequence it is executed in, it defaults to both, which means execute in both InstallUISequence and InstallExecuteSequence.
Now, when a user changes the installation directory in the wizard, this happens in the UI sequence. Obviously, when the installation enters the execute sequence, the value of INSTALLFOLDER is set to [WindowsVolume]Myapp as it was instructed.
So, you have to rework this somehow. Keep in mind the silent installation as well - there's only execute sequence there.
UPDATE instead of what you have, try something like this:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="WindowsVolume">
<Directory Id="INSTALLFOLDER" Name="Myapp">
<Directory Id="BIN" Name="Bin" />
<Directory Id="ICONS" Name="Icons" />
</Directory>
</Directory>
</Directory>
And let the user optionally change the INSTALLFOLDER as you do now.
Additionally to the pitfall with capital letters there is also an other one:
You have to mark the ID of the changeable directory as secure. (At least when the setup runs with admin rights.)
Related to Yonatan's answer with the directory ID MYAPPINSTALLERDIR you have to add this:
<Property Id="MYAPPINSTALLERDIR" Secure="yes" />
Related to the example WixUI_InstallDir in the WiX documentation you have to add this:
<Property Id="TESTFILEPRODUCTDIR" Secure="yes" />
Unfortunately this important fact is not mentioned in the WiX example.
I've been using Wix 3.5 with WixVSExtension to install project item templates for Visual Studio 2010, Visual C# 2010 Express and Visual Web Developer 2010 Express. I'd like now to add support for Visual Studio 11 Beta.
I added registry search and custom actions to find the VS11 installation folders but, when specifying directory and components structure, I'm getting compilation errors like this one:
error LGHT0204: ICE30: The target file 'ewa5nwrn.zip|BasicApplication.zip' is installed in '[TARGETDIR]\CSharp\' by two different components on an LFN system: 'VS2010CSharpProjectTemplates' and 'VS11CSharpProjectTemplates'. This breaks component reference counting.
I have this directory structure:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="VS2010_PROJECTTEMPLATES_DIR">
<Directory Id="VS2010_PROJECTTEMPLATES_CSHARP_DIR" Name="CSharp">
</Directory>
</Directory>
<Directory Id="VS11_PROJECTTEMPLATES_DIR">
<Directory Id="VS11_PROJECTTEMPLATES_CSHARP_DIR" Name="CSharp">
</Directory>
</Directory>
</Directory>
Notice that I have VS2010_PROJECTTEMPLATES_DIR and VS11_PROJECTTEMPLATES_DIR inside TARGETDIR. The error message shows that they are ignored.
The components are defined as follow:
<DirectoryRef Id="VS2010_PROJECTTEMPLATES_CSHARP_DIR">
<Component Id="VS2010CSharpProjectTemplates" Guid="{0976A222-8243-40F2-81AB-84D8F1771840}" Transitive="yes">
<File Id="VS2010BasicApplication" Source="BasicApplication.zip" />
</Component>
</DirectoryRef>
<DirectoryRef Id="VS11_PROJECTTEMPLATES_CSHARP_DIR">
<Component Id="VS11CSharpProjectTemplates" Guid="{A70428F1-AE26-4B07-9F58-D67587B44657}" Transitive="yes">
<File Id="VS11BasicApplication" Source="BasicApplication.zip" />
</Component>
</DirectoryRef>
Is it possible to install the same file into two different directories specified by properties?
Thanks in advance,
aalmada
I don't have any source code to share but I can tell you in general how I've done this in the past.
We decided to "install" the zip files to our main application directory in a Integration folder so that the files would always be available even if VS2008 and/or VS2010 had not been installed at the time of installation. I then used a bunch of built-in properties that exist in the Util extension and a couple custom RegLocator searches to define a bunch of properties related to the location of devenv.exe and ItemTemplate, ProjectTemplates and other directories that we needed in .NET. I then used those properties in CopyFile elements so that MSI would duplicate those files in the Integration folder to the actual VS directories as needed and approriate. Finally I used the WiX QuietExec custom action to call Devenv /setup to register the content with VS. I also used the ProgressText element ( ActionText table ) to make the UI look good while this was all happening as VS devenv can take awhile.
As explained in the wix documentation of the Directory element: when you omit the Name attribute you are creating an alias for the parent Directory element.
This is done by recording "." as the directory name in the installer database, as explained in the windows installer documentation for the Directory table.
In your case, I believe you attempt to change this "." value into a real directory name by setting the VS2010_PROJECTTEMPLATES_DIR and VS11_PROJECTTEMPLATES_DIR properties (because directories are properties) during the installation.
That's fine, but the problem here is that the light linker doesn't know what values you will set these properties to during the installation. It only has the Name attribute value to work with. And at link time, it looks like you are installing the same file twice to the same directory.
To fix this, add Name attributes with different placeholder values to the VS2010_PROJECTTEMPLATES_DIR and VS11_PROJECTTEMPLATES_DIR directory elements.
I ended up adding two base directories, under TARGETDIR, with the Name properties set:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="VS2010" Name="VS2010">
<Directory Id="VS2010_PROJECTTEMPLATES_DIR">
<Directory Id="VS2010_PROJECTTEMPLATES_CSHARP_DIR" Name="CSharp">
</Directory>
</Directory>
</Directory>
<Directory Id="VS11" Name="VS11">
<Directory Id="VS11_PROJECTTEMPLATES_DIR">
<Directory Id="VS11_PROJECTTEMPLATES_CSHARP_DIR" Name="CSharp">
</Directory>
</Directory>
</Directory>
</Directory>
Here is the code for the properties (based on the WixVSExtension code):
<Property Id="VS11DEVENV" Secure="yes">
<RegistrySearch Id="VS11DevEnvSearch" Root="HKLM" Key="SOFTWARE\Microsoft\VisualStudio\11.0\Setup\VS" Name="EnvironmentPath" Type="raw"/>
</Property>
<Property Id="VS11_ROOT_FOLDER" Secure="yes">
<RegistrySearch Id="SearchForVS11RootPath" Root="HKLM" Key="SOFTWARE\Microsoft\VisualStudio\SxS\VS7" Name="11.0" Type="raw"/>
</Property>
<Property Id="VS11_PROJECTTEMPLATES_DIR" Secure="yes">
<RegistrySearch Id="VS11DevEnvForProjectTemplatesSearch" Root="HKLM" Key="SOFTWARE\Microsoft\VisualStudio\11.0\Setup\VS" Name="EnvironmentDirectory" Type="raw">
<DirectorySearch Id="VS11ProjectTemplatesPathSearch" Path="ProjectTemplates" Depth="1" />
</RegistrySearch>
</Property>
<Property Id="VS11_ITEMTEMPLATES_DIR" Secure="yes">
<RegistrySearch Id="VS11DevEnvForItemTemplatesSearch" Root="HKLM" Key="SOFTWARE\Microsoft\VisualStudio\11.0\Setup\VS" Name="EnvironmentDirectory" Type="raw">
<DirectorySearch Id="VS11ItemTemplatesPathSearch" Path="ItemTemplates" Depth="1" />
</RegistrySearch>
</Property>
<CustomAction Id="VS11InstallVSTemplates" Property="VS11DEVENV" ExeCommand="/InstallVSTemplates" Execute="deferred" Return="ignore" Impersonate="no" />
I then found out that the VS2010 templates don't expand correctly under VS11 but that's a different question now... :-/
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.