WiX: Features containing identical directories but must be merged into one unique directory during the installation - 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.

Related

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.

Executing an EXE file in WiX

I try to execute an EXE file from an MSI file in WiX, but I got 1603 error when doing InitializeSetup.
Action ended 12:09:54: InstallValidate. Return value 1.
Action start 12:09:54: InstallInitialize.
Action ended 12:09:54: InstallInitialize. Return value 3.
Action ended 12:09:54: INSTALL. Return value 3.
What is wrong in this WiX Script?
<Product Name='something' Id='11934d63-12d1-4792-829e-046de3bb987e'
UpgradeCode='{a101616a-365c-44a7-bfcb-fafb356c2ea1}'
Language='1033' Version='8.3.4' Manufacturer='something2'>
<Package Id='*' InstallerVersion='200' Compressed='yes' />
<Binary Id="Instalator.exe" SourceFile="d:\Instalator.exe"/>
<CustomAction Id="LaunchFile" BinaryKey="Instalator.exe" ExeCommand="" Execute='deferred' Return='asyncNoWait' Impersonate='no'/>
<InstallExecuteSequence>
<Custom Action='LaunchFile' Before='InstallFinalize'/>
</InstallExecuteSequence>
</Product>
I don't know why, but when I add:
<Directory Id='TARGETDIR' Name='SourceDir'>
<Component Id='MainExecutable' Guid='1193cd63-12d1-4792-829e-046de3bb987e'>
</Component>
</Directory>
<Feature Id='Complete' Level='1'>
<ComponentRef Id='MainExecutable' />
</Feature>
after Package node -> then it works fine. I need to figure out why...
I have some other concerns about what you are doing here, but if you really need to go out of process to an EXE to complete your install, then I'd suggest using the Quiet Execution Custom Action.
You should know though that this isn't a good practice for a number of reasons. 1) It's not declarative, 2) it doesn't support rollbacks. There are others but those are the biggest IMO.
BTW, WiX isn't "scripting". Understand that and you'll understand why not to call EXE's.
Because you are running the exe as a deferred action, it runs in the context of the SYSTEM account. This error is due to the system account not having the required permissions on the file system http://support.microsoft.com/kb/834484.
It is possible to get around this using PowerShell to execute the exe using the -RunAs switch, but this is a bit nasty. It really all depends exactly what you are doing in the exe as to the best course of action. I'm with Mr. Painter, using an EXE should be the last resort.
Another option is to move the exe setup code so that it runs the first time the user runs the app.
Important note for WIX, After completion of all application installation then the .sql file or database files runs through wix or wpf or winform application.

WIX public properties displayed on the UI

We have an installer created using WIX. As part of this install we would like to show the currently selected installation path. I thought this would be much easier than it is, apparently. I have tried using the a public property "INSTALLDIR" (I know we're not using Installshield, this value is a directory ID.)
<Directory Id="INSTALLDIR" Name="AcmeInc">
I can also see where INSTALLDIR gets set when running the install
MSI(EC:6C) Dir (target): Key: INSTALLDIR , Object: C:\Program Files\AcmeInc\
but when I try to show this on the UI using a Text attribute I get "...\." which doesn't even look to be a relative path.
I know there has got to be something simple I'm missing here.
Assuming you're using WiX 3.5 and the MajorUpgrade element - the following should work (I usually use APPLICATIONFOLDER instead of INSTALLDIR - but they should be interchangeable).
First, let's set ARPINSTALLOCATION as described on http://robmensching.com/blog/posts/2011/1/14/ARPINSTALLLOCATION-and-how-to-set-it-with-the-WiX-toolset
<SetProperty Id="ARPINSTALLLOCATION" Value="[INSTALLDIR]" After="CostFinalize" />
Now lets set the selected installation folder to the previous installation folder, if one previously existed that is.
<Property Id="INSTALLDIR" Secure="yes">
<RegistrySearch Id="FindInstallLocation"
Root="HKLM"
Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[WIX_UPGRADE_DETECTED]"
Name="InstallLocation"
Type="raw"
Win64="yes" />
</Property>
And during the UI sequence, we want this value to be set 'early'
<InstallUISequence>
<AppSearch After="FindRelatedProducts"/>
</InstallUISequence>

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.

Using WiX to package an installer with many files

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.

Resources