WIX: Howto set the name of the msi output file dynamically - wix

I want to include some dynamic part in the filename of the msi file my wix projects produce. This dynamic part should be controlled by variables which are part of my wix project and are declared like this:
<?define ProductVersion="7.1.0.1" ?>
Does anybody know about a way of sending that value of that wix variable to the linker to use it as a part of the output filename?
By the way: I'm using Wix3

You could update the OutputName of your .wixproj and use an MSBuild variable to pass through the version number or any other variable you like.
My build script looks like this:
set PRODUCTVERSION=7.1.0.1
MSBuild.exe /p:Configuration=Debug /p:ProductVersion=%PRODUCTVERSION% Installer.wixproj
And my WiX project looks like this:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProductVersion>1.0.0.0</ProductVersion>
<ProjectGuid>{b7415c44-8d59-4ac2-b698-03e399a305e3}</ProjectGuid>
<SchemaVersion>2.0</SchemaVersion>
<OutputName>Installer.$(ProductVersion)</OutputName>
...
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<OutputPath>bin\$(Configuration)\</OutputPath>
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
<DefineConstants>Debug</DefineConstants>
<WixVariables>ProductVersion=$(ProductVersion)</WixVariables>
</PropertyGroup>
...
</Project>
The output would be:
Installer.7.1.0.1.msi

The msi file name is not determined by your wix files, but by the light.exe -out switch. You can use the same value for -out and inside your wix files if you do the following in your build script, assuming it is a batch script:
set an environment variable with set
productversion=1.2.3
Pass -out foo%productversion%.msi to the
light.exe linker
use the same environment variable in
your wix files as
$(env.productversion)

Open *.wixproj (example: Setup.wixproj)
Go to the end of the file.
$(Configuration) = Debug| Release …
Set path of your application on AssemblyFiles.
<Target Name="BeforeBuild">
<GetAssemblyIdentity AssemblyFiles="..\App\bin\$(Configuration)\App.exe">
<Output TaskParameter="Assemblies" ItemName="AsmInfo" />
</GetAssemblyIdentity>
<CreateProperty Value="$(SolutionName)_%(AsmInfo.Version)_$(Configuration)">
<Output TaskParameter="Value" PropertyName="TargetName" />
</CreateProperty>
</Target>
Output = App_1.0.0.0_Debug.msi

I found a couple of great reference posts to do just this operation:
http://blog.tentaclesoftware.com/archive/2009/05/03/38.aspx
and a follow-on with a better method of creating the output file using a pre-build event:
http://blog.tentaclesoftware.com/archive/2010/08/05/99.aspx
I know the links are old, but the method is sound.
Here are the basic steps:
Put the version you wish to use into a file you can access from your project. I use the main executable of my installation because I also bind to that version in my wxs. In my case, since I am building with C# and use SVN, I have a template version of my assembly.cs, assembly.cs.txt, that I run subwcrev on as a pre-build event to create the assembly.cs that gets compiled into my executable (I actually do it in a separate project in my solution). Subwcrev inserts some date and revision information that I use to create a version in the form "major.minor.version.0" where I use "year.month.revision.0" for my version.
Now, I usually just set AssemblyFileVersion with this method, but to be able to use my version number in the wixproj build event referenced in the post above, I also need to set AssemblyVersion since that is what can be accessed with GetAssemblyIdentity. This method would be questionable if I were really using an assembly someone else links to, but for me it is OK since it is in my final application executable.
Follow the steps outlined in the second post (the first post discusses using the binding method for version in wxs and how to unload and edit the wixproj - valuable context for the second post).
Works like a charm!
Update some years later - it looks like the links I referenced are dead. Here is a snapshot of what I changed to make it work.
In my main product wxs file, I changed (some names have been changed or omitted to protect the innocent and the guilty):
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<!-- TODO: Remove the comments around this Component element and the ComponentRef below in order to add resources to this installer. -->
<Component Id="ProductComponent" Guid="DF1AB3A6-17D0-4176-B3AC-C073AC47AA80">
<RemoveFile Id="PurgeAppFolder" Name="*.*" On="uninstall" />
<File Source="$(var.MyApp.TargetPath)" KeyPath="yes"/>
<File Source="$(var.MyApp.ProjectDir)\bin\Release\MyData.dll"/>
<File Source="$(var.MyApp.ProjectDir)\bin\$(var.MyApp.Configuration)\ICSharpCode.SharpZipLib.dll"/>
</Component>
</ComponentGroup>
to
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<!-- TODO: Remove the comments around this Component element and the ComponentRef below in order to add resources to this installer. -->
<Component Id="ProductComponent" Guid="DF1AB3A6-17D0-4176-B3AC-C073AC47AA80">
<RemoveFile Id="PurgeAppFolder" Name="*.*" On="uninstall" />
<File Id="MAINEXECUTABLE" Source="$(var.MyApp.TargetPath)" KeyPath="yes">
<Shortcut Directory="ApplicationProgramsFolder" Id="MYAPPEXEAPF" Name="My App Name" Icon="MyAppIcon.exe" IconIndex="0" Advertise="yes" />
<Shortcut Directory="ApplicationDesktopFolder" Id="MYAPPEXEADF" Name="My App Name" Icon="MyAppIcon.exe" IconIndex="0" Advertise="yes" />
</File>
<File Source="$(var.MyApp.ProjectDir)\bin\Release\MyData.dll"/>
<File Source="$(var.MyApp.ProjectDir)\bin\$(var.MyApp.Configuration)\ICSharpCode.SharpZipLib.dll"/>
</Component>
</ComponentGroup>
and up at the top I added:
<Product Id="C86505BF-303F-4D6B-8F5F-43A57635F85D" Name="My Application Name" Language="1033" Version="!(bind.FileVersion.MAINEXECUTABLE)" Manufacturer="My Software Shop" UpgradeCode="D1C628E5-5478-4DA5-A31A-F9191D5B1544">
Note that the Version is bound to the file version of MAINEXECUTABLE, which is defined in the main product component.
I hope this helps!

If you have several configurations then inside .wixprj file you can do the following
<OutputName Condition="'$(Configuration)' == 'User'">User.Setup</OutputName>
<OutputName Condition="'$(Configuration)' == 'Designer'">Designerr.Setup</OutputName>

Since it's just a file name, why not have a post-build action that renames the file in your build script (assuming MSBuild)?

Do the variables have to be defined in WiX? I'm building my setup binaries from MSBuild, and I've simply set the output file name to MyProject_$(Platform) -- I expect that any MSBuild variable substitution will work equally well.

Product Id="GUID" Name="whatevername $(var.ProductVersion)"

Related

Adventures in Installing a C#/WPF Application (WiX)

I've got a C#/WPF application that is one step up from simple - it's got the main application, a couple of libraries, and then some NuGet packages. If I look in the project's bin/release/netcoreapp3.1 directory, there's a single EXE, a couple of JSONs, and a bunch of DLLs (some of which have PDBs). Nothing too crazy. The client wants an installer which doesn't sound like an unreasonable request.
I first try the installer that comes with Visual Studio 2019 and am astounded at how useless it is. After deleting it, I poke around on the interwebs and discover that WiX seems to be relatively popular. Ok, says I, I will try it.
After installing the WiX Toolset Build Tools (which sounds a bit redundant) and the WiX Toolset Visual Studio Extension, and restarting Visual Studio, it's looking good. I right-click on my solution, select "Add" and "New Project" and see "Setup Project for WiX v3" which looks good. I click Next, name it Installer, and I get this:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="Installer" Language="1033" Version="1.0.0.0" Manufacturer="" UpgradeCode="2995de5a-87ef-443d-a25a-450d39720349">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate />
<Feature Id="ProductFeature" Title="Installer" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="Installer" />
</Directory>
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<!-- TODO: Remove the comments around this Component element and the ComponentRef below in order to add resources to this installer. -->
<!-- <Component Id="ProductComponent"> -->
<!-- TODO: Insert files, registry keys, and other resources here. -->
<!-- </Component> -->
</ComponentGroup>
</Fragment>
</Wix>
Which looks promising. If I try to build it gives me the error "The Product/#Manufacturer attribute's value cannot be an empty string" which is a textbook example of what an error message should look like. Precise, tells me exactly what is happening, and points me in the direction of what I need to do. I'm liking this! I put some text in the Manufacturer attribute, rebuild, and it says "Build: 1 succeeded" and I have an installer. It does give me the warning "The media table has no entries" which makes perfect sense as I haven't told it to install any files.
I start reading the official WiX tutorial and it's painfully verbose. It's not actually a tutorial as much as a treatise on why WiX is the way it is. I don't actually care at this point, I just want the darn thing to work. I want a tutorial, not the entire history of the company. Eventually I find a link for "Getting Started" and I feel a little better. But again, instead of getting me started, I get a lengthy explanation of the internal workings of the system. At this point I DON'T CARE. I want to get a basic installer working, then you can hit me with some details. After several more pages of the author thinking he or she is a university professor in a lecture, I finally get some XML - and it's for a media tag that I may or may not need, it's not at all clear. And, since there isn't a Media tag in the WXS file that was generated for me, I don't know where it goes.
Giving up on the tutorial, I do a Google search for "wix "visual studio" project" and find this page which has a link for "Creating a simple setup" which is perfect. It actually takes me through what I need, step-by-step. I follow the instructions, hit Build, and it says "Build: 1 succeeded". I find the MSI file, run it, there's no UI which seems odd but I can live with that, and it creates a folder in my "Program Files (x86)" folder that has the primary DLL for my project. Progress!
So now I need to include the DLLs constructed from my library projects, and all the DLLs that were included from NuGet packages. It would be cool if I could tell WiX "please include all the files required by my various NuGet packets" but poking around the web, it appears WiX doesn't know how to do that, but that's OK, really all I need is for the installer to include all of the DLLs that end up in my bin/release/netcoreapp3.1 directory.
Trying the obvious wildcard approach:
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<Component Id="ProductComponent">
<File Source="$(var.MyApplication.TargetPath)" />
<File Source="$(var.MyApplication.TargetDir)/*" />
</Component>
</ComponentGroup>
Just gives an error that is not as easy to read as the first error, but does seem to indicate the asterisk is a no-go. If I do a Google search for "wix add all files in directory" returns a whole bunch of different suggestions, all of which are complicated, which seems odd for such a basic thing. But following the instructions in this page, I end up with an installer that almost works. I get the error "Undefined preprocessor variable $(var.HarvestPath)". This page happened to have the answer to that one, and I have a working installer again. But now it's including absolutely everything in that folder, and I only want the DLLs. Doing a Google search for "wix heatdirectory exclude files" gives no answers, it appears this feature is relatively brain-dead.
So here is my current WIXPROJ:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" InitialTargets="EnsureWixToolsetInstalled" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<DefineConstants>HarvestPath=...\Deploy</DefineConstants>
<HeatDefinitions>MySourcePath=..\src\Your.App\bin\Release</HeatDefinitions>
</PropertyGroup>
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProductVersion>3.10</ProductVersion>
<ProjectGuid>4ece284d-9247-4f7a-84a7-34820606a003</ProjectGuid>
<SchemaVersion>2.0</SchemaVersion>
<OutputName>Installer</OutputName>
<OutputType>Package</OutputType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<OutputPath>bin\$(Configuration)\</OutputPath>
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
<DefineConstants>Debug</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<OutputPath>bin\$(Configuration)\</OutputPath>
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
</PropertyGroup>
<ItemGroup>
<Compile Include="Product.wxs" />
<!-- This will be your default one -->
<Compile Include="HeatGeneratedFileList.wxs" />
<!-- This is the Heat created one -->
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MyApplication\MyApplication.csproj">
<Name>MyApplication</Name>
<Project>{6064dca8-d649-4592-bed5-34039bb3d30d}</Project>
<Private>True</Private>
<DoNotHarvest>True</DoNotHarvest>
<RefProjectOutputGroups>Binaries;Content;Satellites</RefProjectOutputGroups>
<RefTargetDir>INSTALLFOLDER</RefTargetDir>
</ProjectReference>
</ItemGroup>
<Import Project="$(WixTargetsPath)" Condition=" '$(WixTargetsPath)' != '' " />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets" Condition=" '$(WixTargetsPath)' == '' AND Exists('$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets') " />
<Target Name="EnsureWixToolsetInstalled" Condition=" '$(WixTargetsImported)' != 'true' ">
<Error Text="The WiX Toolset v3.11 (or newer) build tools must be installed to build this project. To download the WiX Toolset, see http://wixtoolset.org/releases/" />
</Target>
<Target Name="BeforeBuild">
<HeatDirectory Directory="..\MyApplication\bin\$(Configuration)\netcoreapp3.1"
PreprocessorVariable="var.MyApplication.ProjectDir"
OutputFile="HeatGeneratedFileList.wxs"
ComponentGroupName="HeatGenerated"
DirectoryRefId="INSTALLFOLDER"
AutogenerateGuids="true"
ToolPath="$(WixToolPath)"
SuppressFragments="true"
SuppressRegistry="true"
SuppressRootDirectory="true" />
</Target>
<!--
To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Wix.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
All I want is a simple installer that copies over the EXE and associated DLLs. Why is this so difficult? Can WiX do what I need? Or do I need a different installer toolkit?

How to extract NuGet contentFiles to a specific directory?

What I've tried so far:
I've got the following .nuspec file, defining my package (simplified for better readability):
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
.
.
.
<dependencies/>
<contentFiles>
<files include="any\any\config\*.*" buildAction="None" copyToOutput="true" flatten="false"/>
</contentFiles>
</metadata>
<files>
<file src="bin\Assembly.dll" target="lib\net461" />
<file src="bin\Assembly.pdb" target="lib\net461" />
<file src="Config\cf.xml" target="contentFiles\any\any\config"/>
<file src="Config\cf.txt" target="contentFiles\any\any\config"/>
</files>
</package>
As you can see, it contains a compiled Assembly along with it's debug symbols file as well as two content Files in a sub directory.
This results in the following compilation output, where the Assembly.dll is extracted to the output directory as well as the config sub directory (with the two cf.* files in it):
Question:
What I want to do is to move the sub directory config one step up in the directory tree, so it sits next to the output directory - basically maintaining the structure of the file input in the .nuspec file (assuming bin is the output directory of my .csproj):
How can I tell the NuGet package the exact location where I want the contentFiles to be extracted to?
You can achieve this by creating a MSBuild target that moves your config folder one level above the project build output.
Add a .targets file with the following content to your NuGet's build\net461 folder:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Sets up items for target MoveConfigFiles, so that they can be used as Inputs and Outputs. -->
<Target Name="PrepareMoveConfigFiles" AfterTargets="Build">
<!-- ItemGroup wrapped in Target to delay evaluation of contained Items -->
<ItemGroup>
<!-- Contains source locations of files to be moved -->
<ConfigSourceFiles Include="$(OutputPath)\config\**\*.*" />
<!-- Contains target locations of files to be moved -->
<ConfigTargetFiles Include="#(ConfigSourceFiles->'%(FullPath)'->Replace('\config\', '\..\config\'))" />
</ItemGroup>
</Target>
<!-- Moves files from $(OutputPath)\config to $(OutputPath)\..\config,
then deletes $(OutputPath)\config. -->
<Target Name="MoveConfigFiles" AfterTargets="PrepareMoveConfigAndSqlFiles" Inputs="#(ConfigSourceFiles)" Outputs="#(ConfigTargetFiles)">
<Move SourceFiles="#(ConfigSourceFiles)" DestinationFiles="#(ConfigTargetFiles)" />
<RemoveDir Directories="$(OutputPath)\config" />
</Target>
</Project>
The target PrepareMoveConfigFiles is executed after the Build target (which guarantees the NuGet's contents exist in build output), and sets up items used as Inputs and Outputs of the next MoveConfigFiles target. (The items are set inside this target to ensure they are evaluated after the Build target has completed. If the item definitions would be placed below the Project level, they would be evaluated earlier and thus would potentially be empty because the files of the content folder have not yet been deployed.)
The ConfigSourceFiles item just contains the source files, and ConfigTargetFiles takes the paths of all ConfigSourceFiles and replaces \config\ with \..\config\, which leads to the desired target locations.
The MoveConfigFiles target then uses the Move and RemoveDir tasks to accomplish the movement of files and deletion of the original config folder.
It is a weird question...
You have several things to do a nuspec file;
The main are "the nuget packages".
Your nuspec line:
<file src="bin\Assembly.dll" target="lib\net461" />
<file src="bin\Assembly.pdb" target="lib\net461" />
I don't understand Assembly.pdb to distribute it, but is not important.
The goal of this lines are distribute yout dlls in your projects.
When you compile your solution (in your solution dir) it search for this dlls in same root application directory or else check if are register on system (gac mainly).
On the other hand other kind of lines are the "content" into nuget
<file src="Config\cf.xml" target="contentFiles\any\any\config"/>
<file src="Config\cf.txt" target="contentFiles\any\any\config"/>
This is additional content but maybe it may not will be used in the project.
If you want a route system you can use content, but you could have problems using the dlls.
So, I suppose that maybe you would this:
<file src="bin\Assembly.dll" target="contentFiles\any\any\bin" />
<file src="bin\Assembly.pdb" target="contentFiles\any\any\bin" />
<file src="Config\cf.xml" target="contentFiles\any\any\config"/>
<file src="Config\cf.txt" target="contentFiles\any\any\config"/>
And other nuspec example (I would not do this, but, it is possible to do it):
<files>
<file src="content\bin\youtBinayFile" target="content\bin\youtBinayFile" />
<file src="content\Config\YourConfigFile" target="content\Config\YourConfigFile" />
<file src="readme.txt" target="readme.txt" />
</files>
I hope I've helped

Make WIX installer place files in AppData

I'm writing a dapp for ethereum client for windows. In order to make dapp available for user I have to place specific files in the folder in appdata. So I just should place some files in %appdata%\Parity\Ethereum\dapps\mydappname. But I always get weird errors with WIX, the last one is
Error 93 ICE64: The directory dapp is in the user profile but is not
listed in the RemoveFile table.
I have following myapp.wixproj
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" InitialTargets="EnsureWixToolsetInstalled" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProductVersion>3.10</ProductVersion>
<ProjectGuid>8beed2e4-8784-4cb5-8648-cdf55c5defe6</ProjectGuid>
<SchemaVersion>2.0</SchemaVersion>
<OutputName>FairsDapp</OutputName>
<OutputType>Package</OutputType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<OutputPath>bin\$(Configuration)\</OutputPath>
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
<DefineConstants>Debug</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<OutputPath>bin\$(Configuration)\</OutputPath>
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
</PropertyGroup>
<PropertyGroup>
<DefineConstants>HarvestPath=dapp</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Include="Product.wxs" />
<Compile Include="Dapp.wxs" />
</ItemGroup>
<Import Project="$(WixTargetsPath)" Condition=" '$(WixTargetsPath)' != '' " />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets" Condition=" '$(WixTargetsPath)' == '' AND Exists('$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets') " />
<Target Name="EnsureWixToolsetInstalled" Condition=" '$(WixTargetsImported)' != 'true' ">
<Error Text="The WiX Toolset v3.11 (or newer) build tools must be installed to build this project. To download the WiX Toolset, see http://wixtoolset.org/releases/" />
</Target>
<Target Name="BeforeBuild">
<HeatDirectory
DirectoryRefId="INSTALLFOLDER"
OutputFile="Dapp.wxs"
Directory="dapp"
ComponentGroupName="SourceComponentGroup"
ToolPath="$(WixToolPath)"
PreprocessorVariable="var.HarvestPath"
AutogenerateGuids="true" />
</Target>
</Project>
And following wxs:
<?xml version="1.0" encoding="UTF-8"?>
<Wix
xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="Dapp" Language="1049" Version="1.0.0.3" Manufacturer="Me" UpgradeCode="fb09dccc-6606-4b5d-8dcb-28146c28663a" Codepage="1251">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<MediaTemplate EmbedCab="yes"/>
<Feature Id="ProductFeature" Title="FDapp" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="AppDataFolder">
<Directory Id="Parity">
<Directory Id="dapps">
<Directory Id="INSTALLFOLDER" Name="F2"></Directory>
</Directory>
</Directory>
</Directory>
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER"></ComponentGroup>
</Fragment>
</Wix>
I found that I have to use heat task to create a correct files tree, but now I'm stuck with simple task "copy these files on user machine".
I am unfamiliar with this type of application (dapp for ethereum client for windows) - so the advice has to be generic I am afraid.
Per-User Files & Registry Settings: In general deploying files to the user profile and HKCU settings is difficult with MSI. As Chris points out it basically just works for the user installing the MSI, unless you actively add constructs to get files copied to all user profiles, and even then it is sort of clunky.
Approaches: I wrote a long answer a long time ago on the subject: Create folder and file on Current user profile, from Admin Profile (long and elaborate, but without any automagic solutions).
Preferred Approach: Before getting involved in too much complexity, the easiest approach is generally to use your application to copy the userprofile files in place for every user on first launch - instead of using the setup to install user-specific files.
This requires that there is a separate application executable to launch, generally via its own shortcut - which it might not be? It generally does not work for addins for example.
Approach 1: Install template files per-machine and then copy them to each
user's userprofile on application launch.
Approach 2: Alternatively I like to download files directly from a server
or database and put in the userprofile - also on first launch.
Apply Updates?: There are ways to ensure that you can re-copy files if there are
changes to your templates as described here: http://forum.installsite.net/index.php?showtopic=21552 (Feb 2019 converted to WayBack Machine link).
Errors: The specific problem you report has to do with the need for a per-user registry key path and a RemoveFolder entry for all folders targeting userprofile locations:
<Directory Id="AppDataFolder">
<Directory Id="Parity">
<Directory Id="dapps">
<Directory Id="INSTALLFOLDER" Name="F2">
<Component Guid="{77777777-7777-7777-7777-7777777777DD}" Feature="MainApplication">
<RegistryKey Root="HKCU" Key="Software\TestManufacturer\TestApp">
<RegistryValue Name="Flag" Value="1" Type="string" KeyPath="yes" />
</RegistryKey>
<RemoveFolder Id="RemoveINSTALLFOLDER" Directory="INSTALLFOLDER" On="uninstall" />
<RemoveFolder Id="RemoveParity" Directory="Parity" On="uninstall" />
<RemoveFolder Id="Removedapps" Directory="dapps" On="uninstall" />
<File Source="Test.exe" />
</Component>
</Directory>
</Directory>
This is just one of MSI's conventions and quirks. As already stated,
install all files per-machine and copy them to the userprofile with
the application instead. It will dis-entangle them from any setup
interference in the future. Then you do not need to deal with these RemoveFolder issues.
WiX Installer - Children of LocalAppDataFolder installed to wrong location after UAC prompt
I've found and verified that the solution is working when adding a CustomAction which uses PowerShell to iterate over all user profiles and copies a "template appdata folder" to them. The template folder is shipped and installed by the WIX MSI installer to [ProductDir]appdata, e.g "C:\Program Files (x86)\myApplication\appdata".
Here's what I've put to as a CustomAction to the WXS WIX script to achieve this:
<CustomAction
Id="ExecPsXcopyCmd"
Directory='ProductDir'
Execute='deferred'
Impersonate='no'
ExeCommand='[SystemFolder]WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy "ByPass" -Command "$productDir = &apos;[ProductDir]&apos;; $userRoot = (Split-Path $Env:PUBLIC); Foreach ($profile in Get-ChildItem -Path $userRoot -Directory -Exclude &apos;Public&apos; -Force -ErrorAction SilentlyContinue) { $srcPath = Join-Path -Path $productDir -ChildPath &apos;appdata&apos;; $dstPath = Join-Path -Path $profile.Fullname -ChildPath &apos;AppData\Roaming\myApplication&apos;; xcopy /S /E /I /R /H /Y $srcPath $dstPath}"'
Return='check'/>
<InstallExecuteSequence>
<RemoveExistingProducts Before="InstallInitialize" />
<Custom Action="ExecPsXcopyCmd" After="WriteRegistryValues">NOT REMOVE</Custom>
<LaunchConditions After="AppSearch"/>
</InstallExecuteSequence>
This assumes your product installation directory is called "ProductDir".

Include all Files in Bin folder in Wix installer

I'm new in Wix, I succefully create an MSI installer for my project, but my Bin folder have a lot of DLL's files with EXE main file, I want to include all these files with the installer
I found THIS solution, that seems right but unfortunately I can not accomplish this solution in my Wix file, Here's my Wix file:
<Product Id="*" Name="Setup"
Language="1033" Version="1.0.1.0"
Manufacturer="ORDER MS"
UpgradeCode="a4f0a0d0-ae64-4f62-9bb3-efa7e75072e0">
<Package InstallerVersion="200"
Compressed="yes"
InstallScope="perMachine" />
<MajorUpgrade Schedule="afterInstallInitialize"
DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate />
<Feature Id="ProductFeature" Title="Setup" Level="1">
<ComponentGroupRef Id="ProductComponents" />
<ComponentRef Id="ApplicationShortcutDesktop" />
<ComponentRef Id="ApplicationShortcut" />
</Feature>
<Icon Id="Icon.exe" SourceFile="$(sys.CURRENTDIR)\icon.ico"/>
<Property Id="ARPPRODUCTICON" Value="icon.exe" />
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<Component Id="ProductComponent">
<File Source="$(var.Order.TargetPath)" />
</Component>
<Component Guid="A7C42303-1D77-4C70-8D5C-0FD0F9158EB4" Id="CopyComponent">
<CopyFile Id="SomeId"
SourceProperty="SOURCEDIRECTORY"
DestinationDirectory="CopyTestDir" SourceName="*" />
</Component>
</ComponentGroup>
I get this Error:
Error 1 ICE18: KeyPath for Component: 'CopyComponent' is Directory: 'INSTALLFOLDER'. The Directory/Component pair must be listed in the CreateFolders table.
This solution works with WIX 3.11.
To harvest an entire directory, you can use Heat from the WIX toolset. With Heat we can automatically include all files from a given source directory on every build. To do that we first need to edit the Setup.wixproj:
Define the Harvestpath for Heat:
<PropertyGroup>
<DefineConstants>HarvestPath=...\Deploy</DefineConstants>
</PropertyGroup>
Heat will create a .wxs file. So we need to add this file to the compile ItemGroup:
<ItemGroup>
<Compile Include="Product.wxs" /> <!-- This will be your default one -->
<Compile Include="HeatGeneratedFileList.wxs" /> <!-- This is the Heat created one -->
</ItemGroup>
Then execute Heat in the BeforeBuild build target:
<Target Name="BeforeBuild">
<HeatDirectory Directory="..\Deploy"
PreprocessorVariable="var.HarvestPath"
OutputFile="HeatGeneratedFileList.wxs"
ComponentGroupName="HeatGenerated"
DirectoryRefId="INSTALLFOLDER"
AutogenerateGuids="true"
ToolPath="$(WixToolPath)"
SuppressFragments="true"
SuppressRegistry="true"
SuppressRootDirectory="true" />
</Target>
This will generate the HeatGeneratedFileList.wxs every time the WIX installer is built. The directory ..\Deploy has to be set to the directory of the files to include.
The only thing we have to do to include these files in our installer is to edit the main .wxs file (like Product.wxs in this example). Heat will create a ComponentGroup with the given name from above. This component needs to be referenced in the Feature section of the Product.wxs:
<Feature Id="ProductFeature" Title="DiBA Tool" Level="1">
<...>
<ComponentGroupRef Id="HeatGenerated" />
</Feature>
I think you've gotten into a bit of a muddle, if you don't mind me saying so.
CopyFile will copy a file will copy a file from one place on a target machine (the machine where the install is being installed, not your development computer) to another folder on the same machine. I don't think this is what you want.
As Brian suggested you can use Heat to scan a folder and generate some code for you. You can use that in one of two ways:
As a development aid
Run the tool with this kind of command:
heat dir ".\My Files" -gg -sfrag -template:fragment -out directory.wxs
Then, take directory.wxs and use it as source code.
In the build pipeline
You can use the Heat tool in the build pipeline, so that compiling the install will generate the code.
Contrary to Brian's suggestion, if you are using MSBuild I would suggest the HarvestDirectory target. Here's what I have where I am doing something similar:
<HarvestDirectory Include="$(MyHarvestDirectory)">
<InProject>false</InProject>
<PreprocessorVariable>var.MyHarvestDirectory</PreprocessorVariable>
<ComponentGroupName>MyComponentGroup</ComponentGroupName>
<DirectoryRefID>MY_DIRECTORY_ID</DirectoryRefID>
</HarvestDirectory>
This will feed an item into the HarvestDirectory target and make sure it's all handled in the correct way. This code just goes into an ItemGroup in your .wixproj. (If you're not sure how to tweak your project file, check out this video)
Of course, this assumes you are using MSBuild (or Visual Studio, because that uses MSBuild).
Caveat
Having said all this, if the main issue is simply that there are lots of files, I would simply say knuckle down and just define the list. Use the Heat tool as a scaffold if you like, but there's no substitute for just learning the language and working with it. Trying to do things with auto generated code can introduce subtle issues, and it's always simpler to work with a static list.
I do something similar to what you require here during my installation. I need to copy the contents of a folder with 1000+ files in it (the help files).
What I did to solve this is the following:
In the Installer.wixproj I defined the following:
<Target Name="BeforeBuild" >
<Exec Command=""$(WixToolPath)Heat.exe" dir "$(MSBuildThisFileDirectory)\$(Configuration)\bin" -ag -cg BinDir -dr BIN -template fragment -sreg -sfrag -srd -var var.BinDir -o "$(MSBuildThisFileDirectory)\Components\Bin.wxs"" Condition="!Exists('$(MSBuildThisFileDirectory)\Components\Bin.wxs')" />
</Target>
And this will run heat on the $(Configuration)\bin\ dir and generate a wxs file including ALL the files in the bin dir.
This way if you add or delete any binaries in your bin dir it will automatically get picked up when you rebuild your installer (if the Bin.wxs file doesn't exist).
Now you need to make sure you define the variable "BinDir" for wix which points to the bin dir on the build machine. You also need to add the Bin.wxs file to your wixproj as a link (when adding existing file there's a tiny arrow drop down on "Add". Click that and select "Add as link".)
I think there's an actual heat target somewhere in wix but I haven't looked through that enough to know how to use it yet.
As an alternative to the other answers:
There is a Visual Studio extension called Wax (GitHub, Visual Studio Marketplace):
It provides an alternative way to handle all the required files with GUI.
It may be not as sophisticated as command-line tools like heat, but it is much simpler and can be a nice and friendly tool in the beginning, when you just start learning WiX - and want a quickstart.

WIX HeatDirectory Task - Setting the preprocessorVariable

I'm trying to set the preprocessor variable in wix and i'm unable to find an example of this or explanation on how to do it anywhere on the internet, i'm hoping somebody here can explain or show me where im going wrong!
I have tried the example shown here regarding setting var values
http://www.ageektrapped.com/blog/setting-properties-for-wix-in-msbuild/
The documentation for using the HeatDirectory taks in wix can be found here and is not very useful at all!
How do i set the preprocessorVariable to substitute the SourceDir for another variable name?
PreprocessorVariable for heat really need more doc and example... I've spend lots of time making it work too. This is how it works in my wixproj file:
<PropertyGroup>
<DefineConstants Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">HarvestPath=..\distribution\Debug</DefineConstants>
<DefineConstants Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">HarvestPath=..\distribution\Release</DefineConstants>
</PropertyGroup>
<Target Name="BeforeBuild">
<HeatDirectory Directory="..\distribution\$(Configuration)"
PreprocessorVariable="var.HarvestPath"
OutputFile="HeatGeneratedFileList.wxs"
ComponentGroupName="HeatGenerated"
DirectoryRefId="INSTALLFOLDER"
AutogenerateGuids="true"
ToolPath="$(WixToolPath)"
SuppressFragments="true"
SuppressRegistry="true"
SuppressRootDirectory="true"/>
</Target>
All you need is to define the variable. There is no magical "HeatDefinitions" :)
I use Wix v3.10.
No need to explicitly call HeatDirectory MSBuild Task. ItemGroup with special name "HarvestDirectory" can be prepared, and later the "HarvestDirectory" target will process it. *.wsx file is created in the IntermediateOutputPath and is included in Compile list (processed by Candle).
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
...
<MyFinalFolder Condition=" '$(MyFinalFolder)' == '' ">$(MSBuildProjectDirectory)\Final\</MyFinalFolder>
<DefineConstants>FinalFolder=$(MyFinalFolder)</DefineConstants>
</PropertyGroup>
...
<Import Project="$(WixTargetsPath)" />
<Target Name="BeforeBuild">
<!--
No need to explicitly call HeatDirectory MSBuild Task.
Instead follow documentation http://wixtoolset.org/documentation/manual/v3/msbuild/target_reference/harvestdirectory.html, which has sample and
important comment:
This target is processed before compilation. Generated authoring is automatically added to the Compile item group to be compiled by the Candle task.
So, *.wsx file created in the IntermediateOutputPath and included in Compile list (processed by Candle).
The following ItemGroup with special name "HarvestDirectory" can be prepared, and later the "HarvestDirectory" target will process it, see
C:\Program Files (x86)\MSBuild\Microsoft\WiX\v3.x\wix2010.targets
-->
<ItemGroup>
<HarvestDirectory Include="$(MyFinalFolder)">
<DirectoryRefId>INSTALLFOLDER</DirectoryRefId>
<SuppressRootDirectory>true</SuppressRootDirectory>
<SuppressCom>true</SuppressCom>
<SuppressRegistry>true</SuppressRegistry>
<ComponentGroupName>FilesComponentGroup</ComponentGroupName>
<PreprocessorVariable>var.FinalFolder</PreprocessorVariable>
</HarvestDirectory>
</ItemGroup>
</Target>
The wxs file:
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<DirectoryRef Id="INSTALLFOLDER">
<Component Id="cmp9AA09F4A73FA53E2BDDE4B7BB5C91DFB" Guid="*">
<File Id="fil9AA09F4A73FA53E2BDDE4B7BB5C91DFB" KeyPath="yes" Source="$(var.FinalFolder)\7z.dll" />
</Component>
I created an example project on GitHub showing how to successfully use the HarvestDirectory target, which is a higher-level thing that calls the HeatDirectory task. You don't need to directly call the HeatDirectory task yourself. You can see all of the example code here:
https://github.com/DavidEGrayson/wix-example-harvest-directory
Here are the most important parts:
foo.wixproj
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProductVersion>1.0.0</ProductVersion>
<DefineConstants>ProductVersion=$(ProductVersion);ItemDir=items</DefineConstants>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProjectGuid>27e80d9b-d8b6-423a-a6ff-1d9c5b23bb31</ProjectGuid>
<SchemaVersion>2.0</SchemaVersion>
<OutputName>foo-$(ProductVersion)</OutputName>
<OutputType>Package</OutputType>
<DefineSolutionProperties>false</DefineSolutionProperties>
<WixTargetsPath Condition=" '$(WixTargetsPath)' == '' ">$(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets</WixTargetsPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<OutputPath>bin\$(Configuration)\</OutputPath>
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
<DefineConstants>Debug;ProductVersion=$(ProductVersion)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<OutputPath>bin\$(Configuration)\</OutputPath>
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
</PropertyGroup>
<ItemGroup>
<Compile Include="foo.wxs" />
<HarvestDirectory Include="items">
<DirectoryRefId>ItemDir</DirectoryRefId>
<ComponentGroupName>Items</ComponentGroupName>
<PreprocessorVariable>var.ItemDir</PreprocessorVariable>
</HarvestDirectory>
<WixExtension Include="WixUIExtension" />
</ItemGroup>
<Import Project="$(WixTargetsPath)" />
</Project>
foo.wxs
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Name="Foo"
Version="$(var.ProductVersion)"
Manufacturer="Foo Inc."
Language="1033"
UpgradeCode="0c8504c9-4e62-4e2c-9e1c-4fbe1c478b37"
Id="*">
<Package Description="Foo"
Manufacturer="Foo Inc."
Compressed="yes"
InstallerVersion="301" />
<MajorUpgrade AllowDowngrades="no"
DowngradeErrorMessage="A newer version of this software is already installed."
AllowSameVersionUpgrades="no" />
<Media Id="1" Cabinet="cabinet.cab" EmbedCab="yes" />
<Property Id="ARPCOMMENTS">
Foo package.
</Property>
<Property Id="ARPCONTACT">Foo Inc.</Property>
<Property Id="ARPURLINFOABOUT">https://www.example.com/</Property>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder" Name="PFiles">
<Directory Id="INSTALLDIR" Name="Foo">
<Component Id="readme">
<File Id="readme" Name="README.txt" Source="README.txt" />
</Component>
<Directory Id="ItemDir" />
</Directory>
</Directory>
</Directory>
<Feature Id="Software"
Title="Foo"
AllowAdvertise="no"
ConfigurableDirectory="INSTALLDIR">
<ComponentRef Id="readme" />
<ComponentGroupRef Id="Items" />
</Feature>
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLDIR" />
<UIRef Id="WixUI_InstallDir" />
</Product>
</Wix>
There is a $(HeatDefinitions) property you can set to define these in the parent .wixproj file:
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<HeatDefinitions>MySourcePath=..\src\Your.App\bin\Release</HeatDefinitions>
</PropertyGroup>
<ItemGroup>
<Compile Include="Phoenix.wxs" />
<Compile Include="_HeatGeneratedFileList.wxs" />
</ItemGroup>
<Target Name="BeforeBuild">
<HeatDirectory Directory="..\src\Your.App\bin\Release"
OutputFile="_HeatGeneratedFileList.wxs"
PreprocessorVariable="var.MySourcePath" />
</Target>
</Project>
The only existing mention of this magic property I can find (on the entire Internet) is in a single question about Team Build on the wix-users mailing list. I stumbled across it by luck after spending nearly a day tearing out my hair trying to fix undefined preprocessor variable errors.
I found out what it was, after 1 day of trying various things, the link above is correct but to use the var in the heatdirectory task you have to do it like this.
<HarvestDirectory Include="$(ProjectDirectory)\" >
<DirectoryRefId>WEBDIR</DirectoryRefId>
<KeepEmptyDirectories>true</KeepEmptyDirectories>
<SuppressRegistry>true</SuppressRegistry>
<ComponentGroupName>DynamicWebFiles</ComponentGroupName>
<PreprocessorVariable>var.WixDynamicSourceDirectory</PreprocessorVariable>
</HarvestDirectory>
I have a very large installer kit to build in Visual Studio 2013 (includes some tools related to the main project, a windows service and a web application with a non-trivial directory structure). I'm still learning how to use WiX, and am still crafting, testing and refining the WiX project. Currently, I have the harvest tasks set up as build events, using a command of the form
"C:\Program Files (x86)\WiX Toolset v3.9\bin\heat.exe" dir "$(SolutionDir)\MyProjectDir\bin\$(ConfigurationName)" -cg MyComponentRef -ag -dr MYINSTALLDIR -srd -wixvar -var var.MySourceFiles -sreg -out "$(SolutionDir)\Deployment\My Installer Project\ComponentList.wxs" -t "$(SolutionDir)\Deployment\My Installer Project\FileFilter.xslt"
This command just grabs all the files in the project's bin\Debug (or bin\Release) folder, then filters it using an xml stylesheet transform. There are quite a few harvests to gather like this, so maintaining all the source file variables (the "var.MySourceFiles" argument in the command) was becoming tedious and error prone. Initially, I had added the declarations to the project's preprocessor variables, but I wanted something that was a little more "self-contained". Using the helpful tips I found at WiX tricks and tips, I declared a new include file, "PreprocessorVars.wxi" with content
and got the xslt to include it in the output generated by heat.exe with the snippet
<xsl:processing-instruction name="include">
$(sys.CURRENTDIR)\PreprocessorVars.wxi
</xsl:processing-instruction>
The xslt now produces output that looks something like this:
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:wix="http://schemas.microsoft.com/wix/2006/wi">
<?include
$(sys.CURRENTDIR)\PreprocessorVars.wxi
?>
<Fragment>
<DirectoryRef Id="MYINSTALLDIR" />
</Fragment>
<Fragment>
<ComponentGroup Id="MyComponentRef">
<Component Id="xyz" Directory="MYINSTALLDIR" Guid="*">
<File Id="abc" KeyPath="yes" Source="$(var.MySourceFiles)\MyProjectExecutable.exe" />
</Component>
</ComponentGroup>
</Fragment>
</Wix>
which Wix processes without any errors.