Using WiX to package an installer with many files - installation

I have a WiX 3 project that has hundreds of files, and I can't seem to figure out how to include them all using a wildcard. I have tried this:
heat.exe" dir ".\!Build" -gg -ke -template:fragment -out "Files.wxs"
This produces a fragment .wxs file, but I'm not sure what to do with it. Any help would be much appreciated.

Try this command
heat dir "Your_Directory" -gg -ke -template:Product -out "Files.wxs"
It will create this structure in the generated wxs file {Files.wxs}:
<Fragment>
<DirectoryRef Id="Files">
<Component Id="Test.ico" Guid="{YOUR_GUID}">
<File Id="Test.ico" Name="Test.ico" KeyPath="yes" Source="..[path to file]\Test.ico" />
</Component>
</DirectoryRef>
</Fragment>
You should get one for each file, that was in the directory that you ran heat against. Once that is done, you just have to add the wxs file to your project, make sure you have the directory that the directoryref points to is created.

Related

How can I insert Intel64 or x64 in Template Summary in an MSI project of Wix Toolkit?

I am writing a simple code to install a file in the Program Files folder, NOT Program Files (x86)
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFiles64Folder">
<Directory Id="INSTALLFOLDER" Name="Del">
<Directory Id="MyFolder" Name="MyFolder"/>
</Directory>
</Directory>
</Directory>
</Fragment>
<Fragment>
<Component Id="Component1" Directory="MyFolder" Win64="yes">
<File Id="FirstFile.txt"/>
</Component>
</Fragment>
Basically it should create a folder del in Program Files and in it, it should create folder MyFolder which would contain FirstFile.txt
If I do it for Id = ProgramFilesFolder , it works by installing in Program Files (x86)
Changing it to ProgramFiles64Folder gives the following error
ICE80: This package contains 64 bit component 'Component1' but the Template Summary Property does not contain Intel64 or x64.
My Question is from where or how can I change the Template Summary property ?
Thanks in Advance
Arnson's Approach: Do check out the answer from Bob Arnson. Here is an extract from his blog: "It’s typical to want to produce both 32-bit and 64-bit packages from the same WiX source, so a common approach is to make the #Win64 attribute value a preprocessor variable." Hence he uses a compiler switch to compile either x32 or x64-bit version MSI files from the same source.
And no: you can not support both with architectures with one MSI. See this blog from Heath Stewart: Different Packages are Required for Different Processor Architectures.
"Hard Coded" Way: Here is an old sample you can try:
https://github.com/glytzhkof/WiXBitnessX64
Some extracts:
Package element:
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" Platform="x64" />
Component element:
<Component Feature="ProductFeature" Win64="yes">
<File Source="$(env.SystemRoot)\notepad.exe" />
</Component>
From https://www.joyofsetup.com/2010/05/14/working-hard-or-hardly-working/#manually-marking-package-and-component-bitness:
Specify the -arch switch at the candle.exe command line or the InstallerPlatform property in a .wixproj MSBuild project. When you specify x64 or intel64, Candle automatically sets the package and components in the file being compiled as 64-bit.

WIX Heat wrong 'registryvalue' 'codebase' value

I know there are many similar questions about this theme, but none of them could give me an answer.
I'm creating a windows installer for a dll which I developed in VS2019. The installer is created and works correctly on my machine, but not on other machines. The reason for this is that all registry values named 'CodeBase' have the wrong path to the dlls. It is not the installation directory but the release directory of my VS project.
I tried
"C:\Program Files (x86)\WiX Toolset v3.11\bin\heat.exe" dir "$(TargetDir.TrimEnd('\\'))" -suid -sfrag -srd -gg -g1 -cg EA<my project name>Assemblies -var var.$(ProjectName).TargetDir -wixvar -dr INSTALLFOLDER -out "$(ProjectDir)WIX\$(TargetName).wxs"
where INSTALLFOLDER is defined in the 'Product.wxs'
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="my product name" />
</Directory>
</Directory>
</Fragment>
The CodeBase regitry value lines in the generated XIX file all are looking like this:
<RegistryValue Root="HKCR" Key="CLSID\{41D3DB26-8261-303B-ACAF-F4F823FE21BE}\InprocServer32\3.1.1.0" Name="CodeBase" Value="file:///C:/Dev/C#/<my VS project path>/bin/Release/Microsoft.AspNetCore.Http.Features.dll" Type="string" Action="write" />
As you can see, the created path is not the target directory (which is C:\Program Files (x86)\my product name) but the release directory of my VS project.
I also tried replacing INSTALLFOLDER by TARGETDIR and adding the parameter -directoryid TARGETDIR (resp. INSTALLDIR) in the call of heat.exe, but nothing changed.
So I hope that I find someone here who can tell me, what I'm doing wrong. I'd appreciate any help. If you need more information, please tell me. I'm using heat version 3.11.1.2318 with VS2019 on Win10.
I apologize for the anonymization of my product but it is not my property and for now I'm not allowed to publish it.
Jörg
It's sad to have to say so but for me the only solution was to change to another technology and use a visual studio installer project template (imho. much easier to handle) to create my installer.

WiX: Features containing identical directories but must be merged into one unique directory during the installation

I am trying to create an installer for an application. My application contains a root application and a set of sub products that can be installed independently. I would like to create an installer to manage the installation by option.
After the compilation and linking, I get the following structure.
For MyAppRoot:
..\MyAppRoot\bin
..\MyAppRoot\config
..\MyAppRoot\config\http
..\MyAppRoot\config\was
and other sub directory
...
Each sub product has the same organisation:
For MySubPrd:
..\MySubPrd\bin
..\MySubPrd\config
..\MySubPrd\config\http
..\MySubPrd\config\was
and other sub directory
...
So I have a complete set of sub applications (MySubPrd1, MySubPrd2, ... , MySubPrdN).
During the installation, the SubPrd1 must be merged into MyAppRoot application (always installed).
To build my installer, I first harvest all the files in MyAppRoot and MySubPrd1, MySubPrd2, ... , MySubPrdN in order to create dedicated .wxs files associated to each SubProduct.
set ROOT_BINARY=D:\Prj\MyAppRoot
heat dir %ROOT_BINARY% -cg MyRootApp -gg -scom -sreg -sfrag -srd -dr INSTALLLOCATION -out %OUT_BUILD_WXS%\root.wxs -var env.ROOT_BINARY
set ROOT_PRD1=D:\Prj\MySubPrd
heat dir %ROOT_PRD1% -cg MySubPrd1 -gg -scom -sreg -sfrag -srd -dr INSTALLLOCATION -out %OUT_BUILD_WXS%\prd1.wxs -var env.ROOT_PRD1
and the same for the others SubPrd's.
I have a main WXS file used to build my installer, I create a set a feature:
<Feature Id='Complete'
Title='ROOT Application'
Description='The application.'
Display='expand'
Level='3'
ConfigurableDirectory='INSTALLLOCATION'>
<ComponentRef Id="Shortcut" />
<Feature Id="RootApp"
Title="Main Application RootApp"
Description="...."
Level="3">
<ComponentGroupRef Id="MyRootApp" />
</Feature>
<Feature Id="MySubPrd1App"
Title="Option1"
Description="...."
Level="1000">
<ComponentGroupRef Id="MySubPrd1" />
</Feature>
<Feature Id="MySubPrd2App"
Title="Option2"
Description="...."
Level="1000">
<ComponentGroupRef Id="MySubPrd2" />
</Feature>
</Feature>
Everything is OK except during the link, I get many errors:
error LGHT0130 : The primary key 'dir022180FDDE4E69878C4D1206C73EED8D' is duplicated
After a check I found that the error is related to the directories. It seems WiX does not recognise that the features must be merged in the same path. Some of the directory contains exactly the same files (for example, ..\MyAppRoot\config\ws contains exactly the same files as ..\MySubPrd1\config\ws
How can I fix this problem?
You will have to use an XSL transform to clean up the output. heat doesn't understand overlap with other independent executions of heat today.
Where you have files that are common across Products, you could extract those out into Fragments and reference those fragments in each package.

Running copied files in WiX using a custom action

I'm creating an MSI installer using WiX and I have, say, a *.bat file that I'm copying to SomeFolder2 under %temp% (something like the code snippet below...)
...
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id='ProgramFilesFolder' Name='PFiles'>
<Directory Id='MyDir' Name='SomeFolder'>
<!-- %TEMP -->
<Directory Id="TempFolder" Name="TmpFolder">
<Directory Id='MyDir2' Name='SomeFolder2'>
<!-- CREATE THE %TEMP%\SomeFolder2 FOLDER -->
<Component Id='FolderComponent' Guid='{GUID}'>
<CreateFolder />
</Component>
<Component Id='CheckComponent' Guid='{GUID}'>
<File Id='mybat' Name='mybat.bat' DiskId='1' Source='.\mybat.bat' KeyPath="yes">
<Shortcut Id="mybatShcut"
Directory="ProgramMenuDir"
Name="{name}"
WorkingDirectory='INSTALLDIR'
Advertise="yes" />
</File>
</Component>
</Directory>
</Directory>
</Directory>
</Directory>
</Directory>
...
Now, to run this, I have two custom actions (DESTDIR is %TEMP%\SomeFolder2):
<CustomAction Id="SetPath" Property="ThePath" Value="[DESTDIR]\mybat.bat" />
<CustomAction Id="StartAction" Property="ThePath" ExeCommand="" Return="asyncNoWait" />
Then in the install sequence:
<InstallExecuteSequence>
<Custom Action="SetPath" After="{some standard MS action}">NOT REMOVE="ALL"</Custom>
<Custom Action="StartAction" Before="{some other action}">NOT REMOVE="ALL"</Custom>
...
</InstallExecuteSequence>
I've put SetPath to run after a variety of standard actions (for example, PublishProduct) while StartAction will come before another custom action.
When I run the MSI file, I look in the log and ThePath does get set with the correct path. However, when StartAction is run, I get this error:
Return value 1631.
which, according to the documentation, translate to "ERROR_CREATE_FAILED" (the Windows Installer service failed to start. Contact your support personnel). The thing is, the file did get copied to %TEMP%\SomeFolder2 (before the setting of the path and the actual exection, might I add...), but for some reason, it doesn't execute at all (if you do execute it manually or via the command prompt or whatnot, it does execute normally).
I tried putting the same file under ProgramFiles\Some_Directory_For_The_Program. The same thing happens; it gets copied there, but it doesn't execute. Why is this happening?
First off, as long as you'd like to use a file installed by your package in a custom action, you should make it deferred. That is, StartAction CA in your example must be deferred. Also, I try to use QtExec standard CA when I need to run executables from CA.
I hope this helps.

Wix Conditionally Install Component if Registry Key Exists

I have a component I need to install only if a registry key exists which means an application has been installed.
I need to assign the value of the registry key (it is a directory) to a property then use this property to copy files from.
I have the following script so far but get an error "The system cannot find the file '[MYTESTDIR]fileToCopy.dat'."
Any help would really be appreciated.
<Property Id="MYTESTDIR">
<RegistrySearch Id="NetFramework20"
Root="HKLM"
Key="SOFTWARE\TEST\VALUE\1.00"
Name="MyName"
Type="directory" />
</Property>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="TEST" Name="Test">
<Component Id="MyComponent" Guid="E5FF53DE-1739-42c4-BE37-60F810C9CD69">
<Condition>MYTESTDIR</Condition>
<File Id="fileToCopy.dat" Name="fileToCopy.dat" Source="[MYTESTDIR]fileToCopy.dat">
<CopyFile Id="fileToCopy.datCopy" DestinationProperty="WEBSERVICEBINFOLDER" />
</File>
</Directory>
</Directory>
</Directory>
<Feature Id="MyFeature" Title="MyFeature" Level="1">
<ComponentRef Id="MyComponent" />
</Feature>
Based on my reading of the Wix Schema documentation, your problem is that you have the CopyFile element nested under a File element. Ditch the File element and just have the CopyFile sit under the Component:
<Component Id="MyComponent" Guid="E5FF53DE-1739-42c4-BE37-60F810C9CD69">
<Condition>MYTESTDIR</Condition>
<CopyFile Id="fileToCopy.datCopy" SourceName="[MYTESTDIR]fileToCopy.dat" DestinationProperty="WEBSERVICEBINFOLDER" />
</Component>
The way you had it, nested under the File, Wix was looking for the file on your system during the build - rather than setting up a copy command to be run at install time.
MYTESTDIR is a windows installer property which will get its value on the target system when the package is being installed.
However, you are trying to use this property in a Source attribute, which is used to point to files on the system where the setup package is being build.
Obviously that is not going to work. Windows installer properties don't even exist while the Source attribute is being evaluated, so Source can definitely not support such use.
Bryan's answer is the correct solution for what you're trying to do here. Using CopyFile under a File element is not illegal, but it is intended for copying files which you also install. In this case, you want to copy a file which is already on the target system, so the File element is not appropriate.

Resources