How to declare virtual path in .wxs file? - wix

I am quite new in creating setup project using .wxs file.I am using WIX 3.9. I want to change the physical path to virtual path in .wxs file.
Currently .wsx file is using physical path like this -
<Component Id="cmp2CB74B07C01493F593A258BAE09C0B1C" Guid="08CD4620-DE67-4C7A-A97A-212AB5BA5E52">
<File Id="filCF6108D60D7B1865907043E2ABFE30DF" KeyPath="yes" Source="C:\ProjectName\Client\Bin\AjaxControlToolkit.dll" />
</Component>
I want to use virtual path something like this -
<Component Id="cmp2CB74B07C01493F593A258BAE09C0B1C" Guid="08CD4620-DE67-4C7A-A97A-212AB5BA5E52">
<File Id="filCF6108D60D7B1865907043E2ABFE30DF" KeyPath="yes" Source="~\ProjectName\Client\Bin\AjaxControlToolkit.dll" />
</Component>
How could I do this in .wxs file? Please suggest.

Assuming you are already using heat to generate your components, you can specify a variable by using -var <VariableName>. According to the docs this will:
Substitute File/#Source="SourceDir" with a preprocessor or a wix variable (e.g. -var var.MySource will become File/#Source="$(var.MySource)\myfile.txt" and -var wix.MySource will become File/#Source="!(wix.MySource)\myfile.txt".
I use this by running the heat command in the post-build event of the original project, and passing in -var var.$(ProjectName).TargetDir. The resulting File will have Source="$(var.ProjectName.TargetDir)\whatever.dll". As long as ProjectName is added as a reference to the wix setup project then the variable will be resolved properly.

Related

How to update INI file that is marked as read-only (WiX toolset)

I have a small installer project in Wix Toolset. One of the INI files I need to modify might be marked as read-only and the installer refuses to modify it.
Is there any way to force installer to do the INI modification? All I found was permission setting during file installation, however this file is not part of the installation.
My component looks like this:
<Component Id="FooBar.ini" Guid="GUID" KeyPath="yes" Permanent="yes">
<IniFile Id="FooBar.ini" Directory="FOOBARDIR" Name="FooBar.ini"
Action="addLine" Section="Foo" Key="Bar" Value="1" />
</Component>

Installer conditionally picking up files

I'm building a Wix installer and I need two separate versions of said installer. One that picks up the latest development build of the project and one that picks up the latest release build. Currently my fragment looks like this:
<Property Id="Program.ReleaseBuild" Value="0" />
<?define ReleaseBuild = [Program.ReleaseBuild]?>
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="InstallFolder">
<Component Id="TheExe" Guid="GUID_GOES_HERE">
<?if $(var.ReleaseBuild) = 1?>
<File Id="ProjectExe" Source="(Rel Project Path)/program.exe" />
<?else?>
<File Id="ProjectExe" Source="(Dev Project Path)/program.exe" />
<?endif?>
</Component>
</ComponentGroup>
</Fragment>
And I have a transform on the msi that transforms the file after build. But the problem is that the file is picked up on compile time not install time, so both version of the installer end up having the same file contained in them. Any idea how I can conditionally grab a dev file or a rel file in the same wix project?
If you want to create installation packages based on build quality (debug versus release), you can use two product configuration and select the source based on it. This way, you can run msbuild twice, one for each configuration. I don't understand the purpose of the transform you mentioned.
So here are steps you could take to accomplish this:
Create an empty solution.
Add your wixproj to it.
Add your csproj to it.
Add a reference of the csproj to the wixproj.
Modify your File[Source] to use the project reference, this way:
<File Source="$(var.MyProject.TargetPath)" Id="ProjectExe" />
The $(var.MyProject.TargetPath) will automatically get the exe from the correct path.
Create a batch file to run the msbuild twice, one for each configuration, with the following commands:
C:\> msbuild mySolution.sln /p:Configuration=Debug
C:\> msbuild mySolution.sln /p:Configuration=Release
The result will be two installation packages, one for each configuration.

Install component conditionally in Wix

We have a Wix project for our Installer. Is it possible to check if a file exists?
Basically, for some of our config files, we want to pass in the path of the file that is actually not present in our installer script but pass in the path of the file to a public property on command line.
In this case we want the config file component to use this passed in config file during the installation instead of the default config file.
Is this possible?
I have tried something like this:
<?if FileExist([PORTALCONFIGPATH])?>
<File Id='webConfig' Name='web.config' DiskId='1' Source='[PORTALCONFIGPATH]' KeyPath='yes'/>
<?else?>
<File Id='webConfig' Name='web.config' DiskId='1' Source='web.config' KeyPath='yes'/>
<?endif?>
Someway to check that the passed in file name exists then install the passed in file otherwise use the default file.
The WiX FileSearch, DirectorySearch, Condition, and CopyFile elements are what you are looking to use. Create a property, when executing the msi if the property is set then the DirectorySearch and FileSearch elements will look in the path for that property, setting another property (MYFILEEXISTS for example) then if MYFILEEXISTS CopyFile will run with the path.

In Wix, how can I avoid hardcoding a file source for a file that already exists in the installation directory?

In my .wxs file, I have the following file declaration. This file already exists in the installation directory. It is not being copied from the source directory. I need to update it using util:XmlConfig during the install. The XmlConfig part is working.
<File Id="AppConfig" Name="Dynamics.exe.config" Source="C:\Program Files (x86)\Microsoft Dynamics\GP2010\Dynamics.exe.config" />
The problem is I can't assume the file is always going to exist in that same location. I'd really like to simply reference it like so:
<File Id="AppConfig" Name="[#INSTALLDIR]\Dynamics.exe.config" />
However, that fails with the following message:
> light.exe ....
The system cannot find the file 'SourceDir\....\[#INSTALLDIR]\Dynamics.exe.config'
How can I say "the file already exists in the installation directory, use that file during the installation, and do not validate for it now"?
Directories are available with bracket notation as though they were regular properties. If you don't need to install the Dynamics.exe.config but just access it with XmlConfig elements, you can simply reference that directory and don't need to bother with the File element:
<Component Id="Dynamics.exe.config" KeyPath="yes" Guid="*">
<util:XmlConfig Id="Dynamics.exe.config.XmlConfig1"
On="install"
Action="..."
File="[INSTALLDIR]\Dynamics.exe.config"
...
/>
<util:XmlConfig Id="Dynamics.exe.config.XmlConfig2"
On="install"
Action="..."
File="[INSTALLDIR]\Dynamics.exe.config"
...
/>
</Component>

Using Wix how can I deploy one of several web.config files while installing an ASP.net web application

I'm using Wix 3.6 beta from the command line, not as VS projects. I have a web application that is harvested with heat as a directory. This works. I'm using web.config transforms to manage each of the target environment web.config files. These are output with msbuild, this works and keeps things visible in Visual Studio and source control.
I've hit a problem deploying one of the several web.config files which I am manually including in product.wxs as components with conditions. I was expecting to include all components as deployable features and let the conditions select just one as active. For example:
<DirectoryRef Id="wwwroot">
<Component Id="setup_a" Guid="some_guid" >
<File Source="$(var.ConfigSourceDir)\setup_a\web.config" />
<Condition>ENVIRON = setup_a</Condition>
</Component>
<Component Id="setup_b" Guid="some_guid" >
<File Source="$(var.ConfigSourceDir)\setup_b\web.config" />
<Condition>ENVIRON = setup_b</Condition>
</Component>
This didn't create any file renaming, moving or deleting issues, but has the very fundamental problem that multiple web.config files are mapped to the same destination and this gives me a light error of "Product.wxs(xxx) : error LGHT0091 : Duplicate symbol 'File:web.config' found. This typically means that an Id is duplicated. Check to make sure all your identifiers of a given type (File, Component, Feature) are unique."
An alternative approach was to use different named .config files and rename/move one to be the web.config, so something like:
<DirectoryRef Id="wwwroot">
<Component Id="setup_a" Guid="some_guid" >
<File Id="setup_a.config" Source="$(var.ConfigSourceDir)\setup_a.config" />
<CopyFile Id="moveit" SourceDirectory="wwwroot" SourceName="setup_a.config" DestinationDirectory="wwwroot" DestinationName="web.config" />
</Component>
This doesn't throw an error, bot the CopyFile command does nothing at all. I just get setup_a.config in the wwwroot folder.
If I nest the CopyFile inside the File, the copy action then works:
<DirectoryRef Id="wwwroot">
<Component Id="setup_a" Guid="some_guid" >
<File Id="setup_a.config" Source="$(var.ConfigSourceDir)\setup_a.config" >
<CopyFile Id="moveit" DestinationName="web.config"/>
</File>
</Component>
...but nested CopyFile means I can't add (it's disallowed) the Delete="yes" attribute to create a 'move' action. Instead I'm left with both setup_a.config and web.config in the wwwroot folder. Alternatively, if I add a seperate removefile within the same component element it also does nothing:
<RemoveFile Id="removefile" On="install" Directory="wwwroot" Name="setup_a.config"/>
</Component>
So, I'm hoping for a working example of how handle multiple web.config files in a conditional deployment, that doesn't leave files behind. the destination filename of web.config is fixed by the framework and can't be changed. The different configs are also pre-generated outside of wix using config transforms, this also can't be changed but the generated filenames could be anything.
cheers!
You complicate it too much. This should work:
<Component Id="setup_a" Guid="some_guid" >
<File Name="web.config" Id="config_a" Source="$(var.ConfigSourceDir)\setup_a\web.config" />
<Condition>ENVIRON = setup_a</Condition>
</Component>
<Component Id="setup_b" Guid="some_guid" >
<File Name="web.config" Id="config_b" Source="$(var.ConfigSourceDir)\setup_b\web.config" />
<Condition>ENVIRON = setup_b</Condition>
</Component>
Pay attention to a couple of things here:
the File/#Name is the same - that's the target file name you'd like to have (web.config)
the File/#Id is different for each File in order to avoid the light error you mentioned first
the File/#Source can be anything - it just describes what file to take as a source
In this sample light will still complain with warning LGHT1076, but that's just a warning - it pays your attention that conditions MUST be mutually exclusive to avoid problems.
I usually take a different approach. Rather then placing multiple mutually exclusive files into an installer that are tightly coupled to specific instances I put a generic file in the installer and use XML changes to transform the XML with the variation point data such as connection string and what not.
This allows me to make installers that can be deployed anywhere silently just by passing a few properties and the command line.