I am trying to package the binaries of an external application in my installer. This external application is managed by another department. There can be multiple versions installed and in multiple locations (ie Program Files
and Program Files (x86)
). There is a batch file that can find the latest version installed.
What I am trying to do is to dynamically define a preprocessor that matches the location of this external app.
Here is how I proceed.
C:\Program Files (x86)\Foo company\Bar program v3.4
) and saves it in an environment variable (%EXTERNAL_APP_PATH%
).call "%WIX%bin\heat.exe" dir "%EXTERNAL_APP_PATH%" -cg ExternalAppBinaryFiles -dr INSTALLBINDIR -sreg -srd -var var.ExternalAppBinariesSourceDir -ag -sfrag -out "heat_generated.wxs"
heat_var_ExternalAppBinariesSourceDir.wxs
whose content looks like the following:<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?define ExternalAppBinariesSourceDir = "C:\Program Files (x86)\Foo company\Bar program v3.4" ?>
</Wix>
When building the project, I get the following error: heat_generated.wxs(6,0): error CNDL0150: Undefined preprocessor variable '$(var.ExternalAppBinariesSourceDir)'
.
I was expecting that file heat_var_ExternalAppBinariesSourceDir.wxs
which define this preprocessor variable would be sufficient for the heat generated file. Mostly because I do include heat_var_ExternalAppBinariesSourceDir.wxs
in my project files.
My understanding is that heat generated files only resolves preprocessor variables from "global preprocessor variables" defined in the project properties (under Build section, label "Define preprocessor variables").
If I define this preprocessor variable as ExternalAppBinariesSourceDir=C:\Program Files (x86)\Foo company\Bar program v3.4
in the project properties, the solution builds with no error.
However, I do have to hardcode the path which is the opposite of "dynamically find the path of my application".
A possible solution would be to add a custom include file (*.wxi) in the generated heat file. Is there a way to force my heat generated file to include another file? This way I could generate a custom include file with the preprocessor variable defined.
Maybe I am not using heat as intended. If I omit the -var var.ExternalAppBinariesSourceDir
part in the command line, the Source
attribute of <File>
elements are prefixed with SourceDir\
.
The result is a build fail with the following error: error LGHT0103: The system cannot find the file 'SourceDir\myapp.exe'
.
Is there a way to have absolute paths for Source
attributes in a heat generated file?
I would like to keep the process as lite as possible for people that build the installer. For example, double-click the solution file in File Explorer, the solution opens in Visual Studio, right-click the Wix setup project and select Build.
I have looked at many other wix example. I have not found people that have the same use case as mine. Most use cases hardcode a preprocessor with a relative path to the binaries from the project files. Some suggest to change the arguments to candle.exe/light.exe, but like I said, I build from Visual Studio and does not call light.exe or candle.exe directly.
I suspect that I should be able to specify "search paths" for resolving paths that are prefixed with SourceDir\
. Looking at my project properties in Visual Studio, there is a "paths" section, but it is all grayed out. Again, I will need to be able to specify dynamic search paths. The problem is still the same. Also search paths might be problematic if multiple directories have the same filename. This is often problematic when packaging a dll with a standard name.
I am using Visual Studio 2017 and Wix Toolset 3.11.
If you want to use an environment variable as the base for your files, you have a few options. Both MSBuild and WiX provide ways to access environment variables directly:
In MSBuild, environment variables are straight-up available as properties. So you could use $(EXTERNAL_APP_PATH)
in your .wixproj and get the path. In particular, you could use it in a DefineConstants
property in your .wixproj like:
<PropertyGroup>
<DefineConstants>
$(DefineConstants);
ExternalAppBinariesSourceDir=$(EXTERNAL_APP_PATH)
</DefineConstants>
</PropertyGroup>
In WiX, you can directly reference environment variables in the preprocessor using $(env.EXTERNAL_APP_PATH)
. That will accomplish the same as the above but without using $(var.ExternalAppBinariesSourceDir)
as an intermediary.
The option I would choose is a combination of the above two. I'd use BindPaths (a WiX feature designed to allow you to specify where your files are found) along with the MSBuild support for environment variables by adding the following item to your .wixproj:
<ItemGroup>
<BindInputPath Include="$(EXTERNAL_APP_PATH)" />
</ItemGroup>
Then all the file sources rooted in SourceDir\
will automatically search the list of BindInputPaths
from your .wixproj to be found.
The latter option is the most powerful and flexible. But any of the above should get you what you want... assuming you want to use the environment variable. Modifying the above to use an MSBuild property from the command line (or other options) should not be hard either.