Wix installer copy a folder and its content - wix

as for now, what I did was to painstakingly query a directory get the file names and one by one create a and as dependency of the product.
I have tried i am getting errors but I am also confuse as to whether its possible or not.
My directory lies in the same folder as the Wix project and it contains files and subdirectory.
I have like 200 files total and it seems like doing it one at a time is tedious with directories and subdirectory.

Pre-note: The following are my notes from the times when I was using Wix extensively. They are a little old (from around 2017-2018) but may help you for harvesting directories.
When a project contains many files to install, it will take too much time to create file and component elements for all of them. One of the tools that is shipped with WiX (heat.exe) could create these as well.
The general syntax for Heat is:
heat.exe harvestType harvestSource <harvester arguments> -o[ut] sourceFile.wxs
Heat can look at a directory, evaluate all the files in it, and create a .wxs file defining the components needed to install all those.
HarvestType
dir: harvest a directory
file: harvest a file
payload: harvest a bundle payload as RemotePayload
perf: harvest performance counters
project: harvest output of a VS project
reg: harvest a .reg file
website: harvest an IIS website
HarvestSource
It is the path to the directory (relative or absolute) which should not end with a backslash.
Without supplying several arguments, the output of heat is not as helpful as it should be:
All directory element names will be impromptu folders supplied to heat.exe, which will not be after an installation.
All guids are “PUT-GUID-HERE”. Heat can generate its own guids.
Source attributes of all File elements are set to “SourceDir\Filename”.
Components will not be grouped as a ComponentGroup.
To fix these issues, the following arguments should be supplied:
-cg: create a componentgroup with the supplied name
-dr : Name of the directory that will be created on the end-user's computer.
-gg: create GUIDs instead of fillers
-g1: do not use curly brackets for guids
-sfrag: use a single Fragment for everything
-srd: not to harvest the folder itself where files are
-var: a preprocessor variable can be used to insert in place of sourceDir.
-scom: suppress com element harvesting
-sreg: suppress registry harvesting
heat.exe dir “D:\Documents\heatf” -dr MyProgramDir -cg NewFilesGroup -gg -g1 -sfrag -srd -sreg -scom -var “var.MyDir” -out “.\heatfile2.wxs”
Now the components are grouped, each component has Guid, and installed to “MyProgramDir” folder, and file elements use the preprocessor variable supplied.
In order to use this fragment file in the main file, referencing ComponentGroup “NewFilesGroup” under a Feature is enough:
<Feature Id…>
<ComponentGroupRef Id=”MyFilesGroup” />
</Feature>
It should be remembered that every time Heat is run on a directory and -gg flag is set, new Guids will be created for components. If a version is already installed at end users, the guids of the components should never change, as doing so will prevent Windows from accurately keeping track of them. Heat will also create new Id attributes for File and Component elements each time it is used.
Note: If the binary files (dll and exe) are compiled with the flag “COM Visible”, then heat will generate the com class registrations (registry and COM element harvesting) as well in the wsx file.

Related

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

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.

How can I use bindpaths to create a WiX Patch?

I'm using cmake/cpack to build my project with WiX.
cmake runs heat.exe (or something similar) which produces files.wxs that contains the files of my project in the following format:
We'll assume a single file named a.txt inside a folder named "bin". The project is built in NewFolder on the Desktop.
<DirectoryRef Id="CM_DP_bin">
<Component Id="CM_CP_bin.a.txt" Guid="*">
<File Id="CM_FP_bin.a.txt" Source="C:/Users/mindlessbot/Desktop/NewFolder/_CPack_Packages/WIX/packageName/bin/a.txt" KeyPath="yes"/>
</Component>
</DirectoryRef>
After the MSI is created, the whole NewFolder is moved in a different directory (on our server). As a result, when I try to create a WiX Patch using the output .wixpdb, I get the following error:
error PYRO0103 : The system cannot find the file 'C:/Users/mindlessbot/Desktop/NewFolder/_CPack_Packages/WIX/packageName/bin/a.txt'
After some googling, I found out that the .wixpdb contains references to the files, which have changed location, so of course it can't find them. I found a thread where someone provided the commands to use bindpaths, however since I'm using cpack I can't directly call them.
So how should I got about doing this?
Some additional info:
My project contains multiple wxs files (not sure if it makes any difference)
The cpack command is:
path/to/cpack.exe -G WIX --config path/to/config.cmake
The patch command is run separately:
torch.exe -p -xi path/to/oldInstaller.wixpdb path/to/newInstaller.wixpdb -out path/to/patch.wixmst
candle.exe path/to/patch.wxs -out path/to/patch.wixobj
light.exe path/to/patch.wixobj -out path/to/patch/wixmsp
pyro.exe path/to/patch.wixmsp -out path/to/patch.msp -t PatchBaselineID path/to/patch/wixmst
In the event that you would like to build your MSIs on one machine, but build the patch on another, I recommend using the .wixout approach rather than relying on the .wixpdb approach. A .wixout file may be generated by the same WiX Toolset CLI tool that is used to build the MSIs, light.exe. The .wixout
Full documentation on the WiX Toolset linker, light.exe, may be found here. The following is an example use of light.exe to build a .wixout file:
"C:\Program Files (x86)\WiX Toolset v3.11\bin\light.exe" "/path/to/Some.wixobj" "/path/to/Another.wixobj" "/path/to/AndYetAnother.wixlib" -bf -xo -out "/path/to/MyInstaller_v1.wixout"
So now let's breakdown what these command line parameters are for:
"/path/to/Some.wixobj" "/path/to/Another.wixobj" "/path/to/AndYetAnother.wixlib" - These are the compiled outputs of WiX source files, they may be either .wixobj files (generated through candle.exe) or .wixlib files (generated through lit.exe, a way of consolidating .wixobj files into one, shareable library).
-bf - This switch causes all of the files to be bound int the resulting .wixout file. This is what removes the need to have the exact folder structure on the target machine when building patches, because the files are carried with the .wixout file.
-xo - This switch tells the linker to output an XML representation of the MSI, instead of the actual MSI. This is required to use the -bf switch.
-out "/path/to/MyInstaller_v1.wixout" - This tells the linker where to output the .wixout file.
Once you have the capability of generating .wixout files, the torch.exe command can be modified to run based on .wixout files instead of .wixpdb files and their associated folder structures. The following is how I would modify your torch.exe command to use .wixout files as opposed to .wixpdb files:
torch.exe -p -xi "path/to/oldInstaller.wixout" "path/to/newInstaller.wixout" -out "path/to/patch.wixmst"
So, all in all I really like this approach, and generally I make my build process produce both an .msi and a .wixout file. This allows us to cache various builds/versions of our installer as .wixout files, and then the process of creating a patch between various versions of the .wixout files becomes relatively easy.

Heat harvest Payload for ExePackage

I have a Compressed file (Self-extracting) that was included as a ExePackage for the current BootStrapper project I'm working on.
I was asked to Unzip the package before including it in the BootStrapper, so that the end user doesn't have to unzip the file before starting the install process of that package.
Excluding the install application, there is two folders (win32 && win64) that contain 288 files each, I need to create a PayloadGroup out of these files. I started doing this by hand, but after looking at Heat element, I saw that Heat can generate Payloadgroup.
How can I harvest from the directories the Payloadgroup that is required for this package.
Doing this manually is a very long process ...
If using Visual Studio you're supposed to be able to go in your setup project's references and click on the reference in question and set Harvest to true.
If I recall this had some issues, and some online sources said it wasn't working for them, I'm not sure at the moment. Which is why I called heat from the command line in a post-build event. Something like this:
call "$(WIX)bin\heat.exe" dir "$(TargetDir)." -var var.$(ProjectName).TargetDir -dr INSTALLFOLDER -cg Binaries -gg -g1 -ag -scom -sreg -sfrag -srd -o "$(SolutionDir)Setup$(ProjectName)\$(ProjectName).Binaries.wxs"
You can even specify an XSLT transform that filters your generated wxs file. For more info check this helpful blog post
I implemented heat.exe dir -generate payloadgroup in v4.0 (https://github.com/wixtoolset/issues/issues/3470). In v3.x, the -generate option was only supported by project harvesting (heat.exe project ...).

How to generate consistent GUID using wix harvest tool

I am new to using WiX so this my be a dumb question. I am trying to keep the GUID generated by the harvest tool (heat.exe) consistent for files that I am packaging. Everyone seems to say that as long as the file path and sub path remain same heat.exe will generate the same GUID for a file. Unfortunately, I am not seeing that behavior. My workflow is as follows.
Copy all the deliverable files to a staging directory during my build process. Then run heat.exe in the staging directory to generate the component fragment wxs file. Before I copy the file into the staging directory I clear all my old files.
I want to add the generated wxs file into my source control so that I can achieve minor upgrades. If my understanding is correct for me to achieve that I need to have consistent GUID across my builds so that I can track them.
The command line I use for generating the wxs file is as follows:-
c:\foobar\build\>C:\win32\wix-3.7.1224.0\heat.exe dir . -cg MyCG -dr INSTALLDIR -g1 -gg -nologo -sreg -suid -t C:\foobar\src\support\packaging\wix\foo.xslt -var var.foobarSource -out c:\foobar\src\support\packaging\wix\foo.wxs
What I see that the GUID generated are different during each run. Is there was a way for me to keep GUID consistent.
You should specify -ag flag.
-ag
Auto generate component guids at compile time, e.g. set Guid="*".
As long as your file paths are the same, generated GUID's will be the same for files.

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