As WiX is based on the XML, it can be version controlled and you can track the changes made. This is important as tool like InstallShield isn't saved to a text file. If you don't see a difference, you won't be able to debug some bug that comes up some day. If you know there is no difference in source code, you can be sure that there will be no difference in behaviour. If there is , then you can be sure that it isn't come from the installer. That will save a good time of debugging in installer.
Another advantage of WiX is that it is based on the Window Installer. If you write installer for Windows, you can be sure that this tool can fully utilize all the feature the OS provides - like rollback. How can you make rollback without OS supports ? I have no doubt that you can invent a wheel but who will credit you on a new wheel ?
Though unlike other script based installer e.g. NSIS and InnoSetup, WiX is declarative language as it comes from XML. You may find it hard to understand how one element behave. You won't be able to step through the XML. You will spend quite some time scanning through document to figure out which element should be used in what parent and still you need to figure out appropriate attributes. There is no assignment operator. There is no type. No control statement. All these makes quite unpleasant to read XML code. Probably you won't be able to remember why you use one XML element in a place after a month later.
So you have pros and cons. Simply speaking, it is a great tool to fine control the installer. But will take some dedicated mind to develop and maintain.
I am going to show you a simple WiX installer which install number of file and shortcuts. Also prerequisites will be installed also in here. Below is a xml file defines Product.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<?include VersionInfo.wxi ?> | |
<Wix | |
xmlns="http://schemas.microsoft.com/wix/2006/wi" | |
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension" > | |
<Product | |
Id="07C001CE-A3E6-4E91-A063-25D20F293E2D" | |
Name="$(var.Company).$(var.Product)" | |
Language="1033" | |
Version="$(var.Version)" | |
Manufacturer="$(var.Company)" | |
UpgradeCode="B7F1057E-1DE0-4B9A-8AD3-C56EF5E63291" | |
> | |
<Package | |
InstallerVersion="200" | |
Compressed="yes" | |
InstallScope="perMachine" | |
/> | |
<MajorUpgrade DowngradeErrorMessage="A newer version of $(var.Product) is already installed." /> | |
<MediaTemplate /> | |
<Feature Id="ProductFeature" Title="$(var.Product)" Level="1"> | |
<ComponentGroupRef Id="Files" /> | |
<ComponentGroupRef Id="Shortcuts" /> | |
</Feature> | |
</Product> | |
<Fragment> | |
<Directory Id="TARGETDIR" Name="SourceDir"> | |
<Directory Id="ProgramFilesFolder"> | |
<Directory Id="INSTALLFOLDER" Name="$(var.Company)" /> | |
<Directory Id="ProgramMenuFolder"> | |
<Directory Id="MyShortcutsDir" Name="$(var.Product)" /> | |
</Directory> | |
</Directory> | |
</Directory> | |
</Fragment> | |
<Fragment> | |
<ComponentGroup Id="Shortcuts" Directory="MyShortcutsDir"> | |
<Component Guid="1DC95A5B-C1DA-4CC5-9742-23CA84DD9F79"> | |
<Shortcut | |
Id="Shortcuts.$(var.Product)" | |
Name="$(var.Product)" | |
Description="Start the main application" | |
Target="[INSTALLFOLDER]YourApplication.exe" | |
/> | |
<Shortcut | |
Id="Shortcuts.Uninstall" | |
Name="Uninstall" | |
Description="Uninstall $(var.Product)" | |
Target="[System64Folder]msiexec.exe" | |
Arguments="/x [ProductCode]" | |
/> | |
<RemoveFolder Id="RemoveMyShortcutsDir" On="uninstall"/> | |
<RegistryValue | |
Root="HKCU" | |
Key="Software\$(var.Company)\$(var.Product)" | |
Name="installed" | |
Type="integer" | |
Value="1" | |
KeyPath="yes" | |
/> | |
</Component> | |
</ComponentGroup> | |
</Fragment> | |
</Wix> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<Include> | |
<?define Company = "YourCompanyName" ?> | |
<?define Product = "YourProductName" ?> | |
<?define Version = "YourVersionNumber e.g. 1.0.0.1" ?> | |
</Include> |
When a product is installed, it is customary to put a registry value of 'Installed' at the HKCU/Software/Company/Product. This can be used by other installer or program to check existence of a product.
Files feature is defined at a separate file. It contains all the files that will be copied to the target PC. But as there can be quite number of files to copy and manually define all these files can be error prone and tedious, 'Heat.exe' is provided by Wix as harvest tool. To use the heat, we can put relevant files in a directory to simplify the process. Here is a sample batch file that copies product files to a directory and run heat.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@SET TARGET_DIR=%1 | |
@SET SOURCE_DIR=%2 | |
echo Copying install files from %SOURCE_DIR% to %TARGET_DIR% | |
rmdir /S /Q %TARGET_DIR% | |
mkdir %TARGET_DIR% | |
xcopy %SOURCE_DIR%\YourApplication.exe %TARGET_DIR% /Y | |
xcopy %SOURCE_DIR%\*.dll %TARGET_DIR% /Y | |
xcopy %SOURCE_DIR%\*.ocx %TARGET_DIR% /Y | |
xcopy %SOURCE_DIR%\*.lua %TARGET_DIR% /Y | |
mkdir %TARGET_DIR%\AnotherDirectory | |
xcopy %SOURCE_DIR%\AnotherDirectory\*.cfg %TARGET_DIR%\AnotherDirectory /Y | |
rem %WIX% is defined by Wix installation to the bin path of Wix | |
"%WIX%bin\heat.exe" dir %TARGET_DIR% -dr INSTALLFOLDER -cg Files -gg -g1 -sf -srd -var "var.DIR.Main" -out "%TARGET_DIR%HarvetsedFiles.wxs" |
Sole reason of copying files from one place to another is the heat.exe. It doesn't allow to run multiple times to merge harvest files. You run heat once and then you can't update the result xml file to feed on the next heat run. Maybe this is just one way to collect all the files to install. Anyway, you can find people using different technique like using XSLT or others to achieve their own goal.
The above batch can be executed by pre-build event of the project with below command line.
$(ProjectDir)PrepareInstallImages.bat $(SolutionDir)\InstallerPickupFilesInHere $(SolutionDir)\YourProductFilesAreHere
First argument is the target path at where the generated installer pick up files. This argument is evaluated at $(var.DIR.Main) at HarvestFiles.wx like below snippet.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> | |
<Fragment> | |
<DirectoryRef Id="INSTALLFOLDER"> | |
<Directory Id="dir9CA0ED38F3ACC979D4C8ACB87F284C88" Name="AnotherDirectory" /> | |
</DirectoryRef> | |
</Fragment> | |
<Fragment> | |
<ComponentGroup Id="Files"> | |
<Component Id="cmp796784FA6BB90C62DCA64FA754CA9F05" Directory="INSTALLFOLDER" Guid="4F477F99-2508-4416-8856-865D39E75510"> | |
<File Id="fil8F050F2F12393A6A3865323CAF8F3E42" KeyPath="yes" Source="$(var.DIR.Main)\YourApplication.exe" /> | |
</Component> |
But this file will be updated with new id and GUID whenever you run the build. It may not desirable. If you want to keep the change minimized between each version of installer, you'd better copy the content to a new file say Files.wxs and use it to build installer. And later when you need to add a new file or directory, you will pick up those changes from the generated HarvestFiles.wxs and put it to the Files.wxs.
With these files in place in the VS project - Product.wxs, VersionInfo.wxs and Files.wxs - you can build the msi file that can install your product.
Though it is not the end yet. You have your files installed with the msi file but it may not run properly as it may need dependencies installed. You may need to install prerequisites like VC2010 x86 redistribution or .NET Framework 4.0 or others. Time to delve into Bundle.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<?include VersionInfo.wxi ?> | |
<Wix | |
xmlns="http://schemas.microsoft.com/wix/2006/wi" | |
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension" | |
xmlns:bal="http://schemas.microsoft.com/wix/BalExtension" | |
> | |
<Bundle | |
Name="$(var.Company).$(var.Product).Setup" | |
Version="$(var.Version)" | |
Manufacturer="$(var.Company)" | |
Copyright="Copyright (c) 2014 $(var.Company). All rights reserved." | |
UpgradeCode="cac171e6-3bfc-4c2f-acc2-97a8c3e87d60" | |
> | |
<util:RegistrySearchRef Id='SearchVC2010X86Installed' /> | |
<util:RegistrySearchRef Id='SearchVC2010X64Installed' /> | |
<BootstrapperApplicationRef | |
Id="WixStandardBootstrapperApplication.RtfLicense" | |
> | |
<bal:WixStandardBootstrapperApplication | |
LicenseFile=".\license.rtf" | |
/> | |
</BootstrapperApplicationRef> | |
<Chain> | |
<PackageGroupRef | |
Id="VC2010SP1Redist" /> | |
<MsiPackage | |
Id="YourProductId" | |
SourceFile="$(var.OutDir)\YourProduct.msi" | |
/> | |
</Chain> | |
</Bundle> | |
<Fragment> | |
<util:RegistrySearch | |
Id ="SearchVC2010X86Installed" | |
Variable="IsVC2010X86Installed" | |
Root="HKLM" | |
Key="SOFTWARE\Microsoft\VisualStudio\10.0\VC\VCRedist\x86" | |
Value="Installed" | |
Result="value" | |
/> | |
<util:RegistrySearch | |
Id ="SearchVC2010X64Installed" | |
Variable="IsVC2010X64Installed" | |
Root="HKLM" | |
Key="SOFTWARE\Wow6432Node\Microsoft\VisualStudio\10.0\VC\VCRedist\x64" | |
Value="Installed" | |
Result="value" | |
/> | |
<PackageGroup Id="VC2010SP1Redist"> | |
<ExePackage | |
Id="VC2010SP1Redistx86" | |
Cache="no" Compressed="yes" | |
PerMachine="yes" Permanent="yes" Vital="yes" | |
SourceFile=".\prerequisites\vc2010\vcredist_x86.exe" | |
InstallCommand="/quiet /norestart" | |
InstallCondition="(NOT VersionNT64 AND NOT IsVC2010X86Installed)" | |
/> | |
<ExePackage | |
Id="VC2010SP1Redistx64" | |
Cache="no" Compressed="yes" | |
PerMachine="yes" Permanent="yes" Vital="yes" | |
SourceFile=".\prerequisites\vc2010\vcredist_x64.exe" | |
InstallCommand="/quiet /norestart" | |
InstallCondition="(VersionNT64 AND NOT IsVC2010X64Installed)" | |
/> | |
</PackageGroup> | |
</Fragment> | |
</Wix> |
Note that the RegistrySearch is from util which is WixUtilExtension. The basic Wix RegistrySearch isn't working at the Bundle - how come ! - and you should use util:RegistrySearch instead.
For the VC2010 x86 and x64 redist, you can check registry whether those are installed before or not. Refer How to detect the presence of the Visual C++ 2010 redistributable package
The result of this bundle project is an exe file with those prerequisite files contained in addition to the msi file of your product.
Reference : WiX : A Developer's Guide to Windows Installer XML, Nick Ramirez.
No comments:
Post a Comment