WIX: Create a fragment for features - windows

I am creating a Windows Installer MSI using WIX. I want to separate files into different fragments, to have small chunks of code in each wxs file.
Currently I have below files:
- Product.wxs which contains Product, Package, MediaTemplate and Feature elements.
- Directories.wxs which contains folders structures and components to give permissions to user and remove folders on uninstall.
I want to take all my features out of Product.wxs too, and create it's own file, called Features.wxs.
Inside Directories.wxs, I have below line of code:
<Component Id="Component1" Guid="PUT_GUID_HERE" Directory="Subfolder">
<CreateFolder>
<util:PermissionEx GenericAll="yes" ChangePermission="yes" Delete="yes" DeleteChild="yes" User="Users"/>
</CreateFolder>
</Component>
With having above code, since I am referencing this Component in a Feature element, I am certain that my Product.wxs and Directories are linked.
But when I create a Feature.wxs file and move below lines of code to it:
<Feature Id="CreateDirectoriesFeature" Title="Feature1" Level="1">
<ComponentRef Id="Component1"/>
</Feature>
I don't know how/where to reference this fragment/features in my Product.wxs.

Create a FeatureGroup with FeatureRef elements and then in Product.wxs use a FeatureGroupRef to bring into scope.

I put my Feature stuff in a different file as well, and the only thing that I need to reference them is the Feature's ID.
Here's a code example. Here's an entire feature file that I have:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<Feature Id="Feature_Name"
Level="1">
<ComponentRef Id="Component_Name"/>
</Feature>
</Fragment>
</Wix>
And here's how I reference it in Product.wxs. I'll show you the entire file from the MajorUpgrade element to its bitter end:
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate EmbedCab="yes"/>
<FeatureRef Id="Feature_Name"/>
</Product>
</Wix>
That's it. Maybe you're not using FeatureRef?

Related

Not able to execute the bat files from WIX bundle

I was creating a wix installer for my product.
I need to run mainly 3 sections in the installer.
Install the postgres database as pre-requisites
Install my product(I have created a msi for my product using wix)
Execute the database scripts(batch file which in turns executes some sql files).
For these steps, I created a wix bundle, and executed the exe package for the postgres database.
Then I executed the msi package for my product. And finally I need to run the batch file. So I used the same bundle and added exe package as shown below
< Chain >
<ExePackage Id="PostgresInstaller" ......>
</ExePackage>
<MsiPackage Id="MyProductPackage" ...... >
</MsiPackage>
<ExePackage Id="DatabaseScipts"
DisplayName="DatabaseScipts"
Description="Configuration files for database"
SourceFile="Scripts\create.bat"
/>
</Chain>
But the problem is, the create.bat file inturns calls some sql files from some other directory. I have added those files in bundle project. But it is not
coming with final bundle exe. Is there any other way to include the sql files in the bundle.
My aim is to execute the bat having sql files after the msi package installer.
In product.wxs file, make sure you include all the files needed.
<!-- Step 1: Define the directory structure -->
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLDIR" Name="My Application Name"/>
</Directory>
</Directory>
<!-- Step 2: Add files to your installer package -->
<DirectoryRef Id="INSTALLDIR">
<Component Id="create.bat" Guid="PUT-GUID-HERE">
<File Id="create.bat" Source="Scripts\create.bat" KeyPath="yes"/>
</Component>
//Other required files in similar ways
</DirectoryRef>
In product.wxs page (not bundle.wxs), add a custom action something like this:
<Fragment>
<CustomAction
Id='RunDbScript'
Directory='INSTALLDIR'
Execute='commit'
Impersonate='yes'
ExeCommand='"[SystemFolder]cmd.exe" /C "[INSTALLDIR]create.bat"'
Return='check'/>
</Fragment>
And add install execute sequence:
<InstallExecuteSequence>
<Custom Action="RunDbScript" Before="InstallFinalize">NOT REMOVE</Custom>
</InstallExecuteSequence>

Prevent content files to be added on Nuget restore

We have some executables which we need to create our setups. So we have packed
the external dependencies which are some .exe files into a nuget package. But on NuGet restore they are added to project root.
How can we achieve this ?
Have searched around but haven't found any solution so far.
Since we use nuspec file, this is what i have it as:
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>VCRedistributable</id>
<version>$version$</version>
<title>VCRedistributable</title>
<authors>--</authors>
<owners>--</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>InstallVCRedistributable assemblies</description>
<contentFiles>
<files include="**" exclude="**" buildAction="None" copyToOutput="false"
/>
</contentFiles>
</metadata>
<files>
<file src="VC\x86\*.*" target="content\x86" />
<file src="VC\x64\*.*" target="content\x64" />
</files>
Any ideas ?
Prevent content files to be added on Nuget restore
You should target to the tools folder instead of content folder.
So, your .nupsec file should be:
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>VCRedistributable</id>
<version>$version$</version>
<title>VCRedistributable</title>
<authors>--</authors>
<owners>--</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>InstallVCRedistributable assemblies</description>
</metadata>
<files>
<file src="VC\x86\*.*" target="tools\x86" />
<file src="VC\x64\*.*" target="tools\x64" />
</files>
</package>
That because the content directory is a convention-based working directory, which contents are copied to the project root:
Convention-based working directory:
Besides, if you nuget package just include external some .exe files, you do not have to add the contentFiles label, this label is used for the content file for packagereference.
<contentFiles>
<files include="**" exclude="**" buildAction="None" copyToOutput="false"
/>
</contentFiles>
If you are interested in, you can check this document for some more details.
Update:
Is it good convention to create our own folder structure other than
NuGet defined since based on the tools folder description from above
it seems they will be accessible via Package Manager Console.
Of course, you can use your own folder structure other than NuGet defined. But you need to notice that there will be a limit to do this. You can NOT just include your own folder structure, you need also need add a NuGet defined folder structure in your .nuspec, otherwise, nuget will install failed with the error like:
Could not install package 'MyCustomPackage 1.0.0'. You are trying to
install this package into a project that targets
'.NETFramework,Version=v4.6.1', but the package does not contain any
assembly references or content files that are compatible with that
framework.
Because nuget did not detect that you added assembly references or content files to the project.
Hope this helps.

How to read content of text file in Wix Toolset Bootstrapper

I am using the WiX toolset to build an installer, I want to read the version from a text file. The text file is located in mybootstrapper like below:
below is the code where i want to read the content of text file
<Bundle IconSourceFile='product.ico'
Name="Retail Grip"
Version="Version.txt" <!-- i know this is not correct -->
Manufacturer="Company Name"
UpgradeCode="PUT-GUID-HERE">
Oh, it is a WiX bundle - and that's "Wax"? I hear it is a WiX tool of sorts? I am not sure exactly how it works (screenshot down the page in that link). Maybe there are restrictions on the use of compiler variables when using it?
I wrote the below before I saw that Wax file and I thought you had a normal WiX source and not a bundle source. Either way, let me add what I wrote and see if it helps. Similarities.
Also: Neil Sleightholm's WiX Burn Template (towards top). Give that link a spin first please.
In a regular WiX file you could use a pre-processor variable: $(var.CurrentVersion) (compiler variable). Something like this:
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?define UpgradeCode="PUT-GUID-HERE"?>
<?define CurrentVersion="1.0.0.0"?>
<Product Id="*" Name="Sample" Language="1033" Version="$(var.CurrentVersion)"
Manufacturer="Someone" UpgradeCode="$(var.UpgradeCode)">
<...>
You can put the variables in its own "include file": Variables.wxi.
<Include>
<?define UpgradeCode="PUT-GUID-HERE"?>
<?define CurrentVersion="1.0.0.0"?>
</Include>
Larger sample here for this approach (do have a quick skim of this one).
And then include the file in your main WiX source:
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?include Variables.wxi ?>
<Product Id="*" Name="Sample" Language="1033" Version="$(var.CurrentVersion)"
Manufacturer="Someone" UpgradeCode="$(var.UpgradeCode)">
<...>
There are also localization variables: WiX (Windows Installer Xml), Create universal variables - link time variable resolution (light.exe), as opposed to the compile time resolution of pre-processor variables (candle.exe). Some context.
Some Relevant Links:
Localization Variables in use: Wix toolset license agreement multi-languages issue
How to make Win64 attribute as a variable in wixlib?
https://helgeklein.com/blog/2014/09/real-world-example-wix-msi-application-installer/
With WiX Include files, you can keep simple values separate from the bulk of the WiX markup. With WiX preprocessing, you can define named substitution values used in attributes or text nodes or conditional compilation, and then refer to them as $(var.name).
-- Version.wxi --
<?xml version="1.0" encoding="utf-8"?>
<Include>
<?define Version="1.2.3" ?>
</Include>
-- Bundle.wxs --
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?include "Version.wxi" ?>
<Bundle
Version="$(var.Version)"
…
It is sometimes convenient to generate include files in a source project in anticipation of them being used downstream, or generate them as the first steps of a WiX project.
I see you are using Visual Studio for your WiX Bootstrapper project. A Visual Studio project is a specialized MSBuild project (as are most types of Visual Studio projects). That means you can put general MSBuild things into the project file. You can open the .wixproj file as an XML file in an XML editor (such as Visual Studio).
MSBuild allows you define new tasks either from an external DLL or inline, using a .NET language. In this case, a few lines of C# will do fine to define the task. Then you would invoke it before the main build tasks. Like many build systems that use MSBuild, WiX ensures the target BeforeBuild is executed before it gets to work. So, you just have to define BeforeBuild.
The task is named CreateVersionWxi.
<UsingTask
TaskName="CreateVersionWxi"
TaskFactory="CodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v12.0.dll">
<ParameterGroup />
<Task>
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
<Using Namespace="System.Xml.Linq" />
<Code Type="Fragment" Language="cs"><![CDATA[
var version = File.ReadAllText("version.txt");
var wxi =
new XDocument(
new XComment("*** GENERATED FILE. DO NOT EDIT ***"),
new XElement("Include",
new XProcessingInstruction("define", "Version='" + version + "'")));
wxi.Save("version.wxi");
]]></Code>
</Task>
</UsingTask>
<Target Name="BeforeBuild">
<CreateVersionWxi />
</Target>
Finally, if you add version.txt to your project with its Build Action, say, as Content, the project will be seen as needing to be rebuilt whenever version.txt changes. That will help if you have the WiX project open in Visual Studio while you are externally changing version.txt.
You don't need to add Version.wxi to your project but doing so increases its visibility to future maintainers.
Tip: Some Visual Studio users are more familiar with the Build Events on the project pages. To clue them in, you could enter this as a pre-build event command line: REM See the BeforeBuild target in the .wixproj file

how to include a folders containing images to product.wxs file in wix project

I am new to WiX, I am facing a problem in adding a folder which contains necessary images and icons to wix project, I am able to include required dlls and files using
<Component Id='HelperLibrary'>
<File Id='HelperDLL' Name='Helper.dll' />
</Component>
Any help will be appreciated.
As you are working with a .NET project, adding of DLLs to the WIX installer from the project output folder of the referred project can be done as following by using the TargetDir item:
<Component Guid="*" Directory="BinFolder">
<File Id="BinFolder.Helper.dll" Source="$(var.ProjectToPackage.TargetDir)Helper.dll"/>
</Component>
Similar way, you can add other files from the project whihch doesn't come under the project output can be added as following using the ProjectDir item from the referred project:
<Component Guid="*" Directory="ImageFolder">
<File Id="ImageFolder.Icon.jpg" Source="$(var.ProjectToPackage.ProjectDir)Icons\Icon.jpg" />
</Component>
Note: Here it is expecting that your WIX project is having reference to the project to be packaged and the project reference is named as ProjectToPackage in this example above.

What is the proper way to remove/uninstall specific packages from my WiX Burn bundle?

I have packages that I would like to remove from subsequent releases. What is the proper way to have this package uninstalled from user machines during future updates? (IE: Remove Package_B below).
I have tried removing the package entry from the bundle and progressed the bundle version, which was the answer in this SO question, but that did not work for me.
<?xml version="1.0">
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Bundle>
<Chain>
<ExePackage
Id='Program_A'
SourceFile='$(var.Program_A.TargetDir)\Program_A.exe'
Cache='no'
Permanent='yes'
PerMachine='yes'
Vital='yes'
/>
</ExePackage>
<ExePackage
Id='Program_B'
SourceFile='$(var.Program_B.TargetDir)\Program_B.exe'
Cache='no'
Permanent='yes'
PerMachine='yes'
Vital='yes'
/>
<RollbackBoundary/>
<MsiPackage
Id='Microsoft_InstallerPkg1'
SourceFile='$(var.Microsoft_InstallerPkg1)'
Cache='yes'
Permanent='yes'
Vital='yes'
>
</MsiPackage>
<RollbackBoundary/>
<Chain>
</Bundle>
</Wix>
Try this
Include same exe packages inside new bundle.
Set DetectCondition to
detect existence of to be deleted package. //It must be evaluates to
true.
Provide UninstallCommand. //It is also necessary otherwise
the package will be executed with default params (which are normally
install by default)

Resources