How to define a dynamic preprocessor in wix for heat generated files - wix

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.
In the Pre-build Event Command Line of the wix project, I call a batch file.
This batch file finds the path to the external app (ie C:\Program Files (x86)\Foo company\Bar program v3.4) and saves it in an environment variable (%EXTERNAL_APP_PATH%).
Then I call heat with: call "%WIX%bin\heat.exe" dir "%EXTERNAL_APP_PATH%" -cg ExternalAppBinaryFiles -dr INSTALLBINDIR -sreg -srd -var var.ExternalAppBinariesSourceDir -ag -sfrag -out "heat_generated.wxs"
Then I generate a custom file 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.

Related

MSBUILD Dynamically Create Config XML Dotfuscator

I am trying to obfuscate bunch of files in a directory and every build there are more and more files being generated. I would like to know if there is a way I can dynamically create the Dotfuscator configuration xml file using a MSBUILD task that will generate the xml file every time there is a new file added to the directory?
This might be a good time to use the Directory input. Rather than representing a single assembly (.exe or .dll), this type of Dotfuscator input captures all the assemblies in a directory. When the contents of the directory change, Dotfuscator's build will automatically pick up any new assemblies.
To make a Dotfuscator config file with a Directory input, open the GUI and add an input as you normally would (directions for Community Edition's GUI and for Professional Edition's standalone GUI), but instead of selecting a file from the Browse... dialog, just navigate to the directory and click "Open" while the "File name" is still listed as "Folder Select". Then, save your configuration.
From now on, whenever you run Dotfuscator (whether from the standalone GUI, the command line, the Visual Studio integration, or the MSBuild task), all assemblies in the directory will be processed as input.
Note: If you look at the config file itself, you might be surprised that it will still list individual assemblies:
<input>
<loadpaths />
<asmlist>
<package refid="19e1b0c5-7221-476f-af4b-bafef68edc95">
<file dir="C:\code\BasicTestApp\BasicTestApp\bin" name="Debug" />
<asmlist>
<inputassembly refid="a6da5d8d-c181-4103-840d-d8cc7c85937a">
<option>honoroas</option>
<option>stripoa</option>
<option>transformxaml</option>
<file dir="" name="BasicTestApp.exe" />
</inputassembly>
<inputassembly refid="df84dad0-fbe8-49ab-b8c8-9fb59e706785">
<option>honoroas</option>
<option>stripoa</option>
<option>library</option>
<option>transformxaml</option>
<file dir="" name="ClassLibrary.dll" />
</inputassembly>
</asmlist>
</package>
</asmlist>
</input>
Despite this layout, Dotfuscator will process all assemblies in the C:\code\BasicTestApp\BasicTestApp\bin\Debug directory when it runs a build based off this config file, not just those two listed.
The assembly elements in the config are just there so that you can still make rules against individual assemblies in the GUI (e.g., to make one assembly be in Library Mode).
The list represents the state of the directory when the GUI last modified the config.
Disclaimer: I work for the Dotfuscator team, and am answering this question as part of my job.
Additional note due to clarification in the comments: the directory package has a feature where you can exclude certain assemblies from obfuscation. These assemblies will be treated as a Package Artifact and just copied from input-to-output without modification. Any obfuscated assemblies that refer to these excluded assemblies will still be processed correctly.
To do this in the GUI, right-click on the assembly within the package, and select "Exclude assembly from package". Or, if you'd prefer to edit the config file, add the following <option> tag as a child of each relevant <inputassembly> tag:
<option>artifact</option>
The latest Dotfuscator version 4.41.1 has the latest flag
true
This will generate the Dotfuscator config file if the file is missing. Also you can add this to the csproj as documented in the latest getting started guide https://www.preemptive.com/dotfuscator/pro/userguide/en/getting_started_protect.html

Can I specify fileloggerparameters in msbuild project file

I see that you can use the /fileLogger and /fileloggerparameters command line arguments in msbuild to specify things like the location of the log file. Is there any way to specify this information in the Project or PropertyGroup section of the project file? I have all my other project properties imported via an include file. I really don't want to have to one set of properties in an include file and then another set that is specified on the command line.
As far as I'm aware just VC projects has ability log build into separate files. But you have to build it through devenv :-(
and you don't have control over other logging parameters.
Microsoft.Cpp.Default.props
<ItemDefinitionGroup>
<BuildLog>
<Path>$(IntDir)\$(MSBuildProjectName).log</Path>
</BuildLog>
</ItemDefinitionGroup>
Other ugly way is to execute build of each project via
<Exec Command="msbuild.exe project /fl /flp...." />
I guess you want to avid it.
I'm think it could be possible to create custom distributed file logger to do this but I sill don't have it working properly.

Using heat.exe tool output file in setup project

I generate setup file list using heat.exe but I can't find out how I can use it in my wix setup project.
Can I import output file to my setup project?
MSBuild supports using wildcards (**, * and ?) to specify a group of files as inputs instead of listing each one separately. If you add something like the following to your project file, every wxs file in the same directory as your project file will be included in your build.
<ItemGroup>
<Compile Include="*.wxs" />
</ItemGroup>
According to this bug, Votive (WiX VS package) does not yet support wildcards, although it does work on my machine. Your mileage may vary. This, of course, is not an issue if you're not using Visual Studio to work on your setup.
Another solution would be to create the file and add it to your project file. Every time you'd build your setup, you would call heat and overwrite the file.
Afterwards, you just need to reference one of the generated elements in your setup to import the fragment. You can do this by using the -cg switch in your heat command.

How can I register a COM-Object with Windows Installer XML

I have got the following Problem: In my WiX Setup I need to register a COM Object. I have got a ".tlb" File and a ".dll" File (in my example: XYCommon.dll and a XYCommon.tlb)
Now I want to make the Setup to Register the TLB.
How Can I do this in Windows Installer XML ?
My first try was to work with a CustomAction and Open "Regasm.exe" and Register it - but I dont know how I can bring the Regasm.exe to Register the XYCommon.tlb
Any Help ? Thanks
Use the WiX harvester utility called Heat. It will generate the WiX source that contains the necessary registry entries to correctly register (and unregister) your COM objects.
Will your COM objects (XYCommon.dll and XYCommon.tlb) change often?
If no: then just run Heat on those two files (read up on the command-line parameters and experiment with them to get the desired output), clean them up if necessary, and add them to your project.
If yes:
If you want to try to run Heat as a Visual Studio prebuild event, this SO answer should give you some guidance: Add a folder to installer in wix not files?
Sometimes when you use Heat you still need to tweak then generated .wxs files a little. Therefore you may want to write some wrapper utility that calls heat on a list of files or a directory, then cleans up the output how you would like. Then you can add the utility to your build process.
Example of using heat on an individual file:
heat.exe file myFile.dll -gg -g1 -suid -svb6 -out myFile.wxs
Example of using heat on an entire directory:
heat.exe dir myDirectory -gg -g1 -suid -svb6 -out myDirectory.wxs
Breakdown of some of the parameters I used:
heat.exe: you'll have to specify the full path to the executable if it's not in your path
file myFile.dll: specify that you want to run heat on the single file, myFile.dll
dir myDirectory: specify that you want to run heat on the directory, myDirectory
gg: generate guids for the components
g1: generate guids without the curly braces
suid: suppress creating uniquely generated ids for files and components, instead use the file names as the ids
svb6: suppress harvesting COM information for the VB6 runtime. Note: If you want to register COM objects that use the VB6 runtime, you need to use this flag so that you do not overwrite your VB6 runtime registry entries

How can I read the OutputDirectory property from a vcproj (2008) file in a wrapper script?

I am trying to write an MSBuild wrapper script that builds a vcproj (well, a solution containing vcproj files) and then copies the output of a particular vcproj file into a special "package" directory that is in turn published out to a file share. I need to do this for several Configurations and Platforms (Debug, Release, Win32, x64). Ideally, I would like to be able to read the "OutputDirectory" from the vcproj file for a particular Configuration/Platform so that I can then copy its contents. With csproj files, this is simple, as I can "import" the csproj file into my MSBuild wrapper script and then read the "OutputPath" property. Unfortunately, I cannot import vcproj (for VS 2008) files into an MSBuild script, as they are not MSBuild-compatible, so that approach does not work.
Does anyone know a way that I can read the value of the "OutputDirectory" property form a vcproj file? Please note that I do not want to use XPath and roll this myself, as the raw OutputDirectory property looks something like "$(ProjectDir)\bin\$(ConfigurationName)\$(PlatformName)". I want all those macros expanded for me, just as would be done when running vcbuild.
You can extract OutputPath for each Configuration in separate property files like
Debug.Properties, Release.Properties, etc and then import appropriate one using directive in both vcproj and msbuild script files.
<Import Project="Debug.Properties" />
Or Dynamically depends on Configuration:
<Import Project="$(ConfigurationBasedPropertiesFile)" />
Set value of the property $(ConfigurationBasedPropertiesFile) considering current Configuration