error PYRO0227 when trying to create WiX patch - wix

I'm trying to create a patch using WiX 3.6 following this example containing 2 C# projects (executable and library). But I'm getting this error:
warning PYRO1079 : The cabinet 'RMT.cab' does not contain any files. If this patch contains no files, this warning can likely be safely ignored. Otherwise, try passing -p to torch.exe when first building the transforms, or add a ComponentRef to your PatchFamily authoring to pull changed files into the cabinet.
error PYRO0227 : The transform being built did not contain any differences so it could not be created.
Executed commands:
set w="c:\Program Files (x86)\WiX Toolset v3.6\bin\"
%w%torch.exe -p -xi 1.0.0.0\PatchMe.Installer.wixpdb 1.1.1.1\PatchMe.Installer.wixpdb -out Patch\Diff.wixmst
%w%candle.exe Patch.wxs
%w%light.exe Patch.wixobj -out Patch\Patch.WixMsp
%w%pyro.exe Patch\Patch.WixMsp -out Patch\Patch.msp -t RTM Patch\Diff.wixmst
Directories "1.0.0.0" and "1.1.1.1" contain output of two different versions of same projects (changed AssemblyVersion and some code changes).
Patch.wxs file:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?include Variables.wxi ?>
<Patch AllowRemoval="yes"
Manufacturer="$(var.Manufacturer)"
DisplayName="$(var.ProductName) $(var.ProductVersion)"
Description="Small Update Patch"
Classification="Update"
TargetProductName="$(var.ProductName)"
>
<Media Id="5000" Cabinet="RMT.cab">
<PatchBaseline Id="RTM">
</PatchBaseline>
</Media>
<PatchFamilyRef Id="SamplePatchFamily"/>
</Patch>
<Fragment>
<PatchFamily Id="SamplePatchFamily" Version="$(var.ProductVersion)" Supersede="yes">
<ComponentRef Id="cmp981D9885AA29DD578D66D32ED919EBFB"/>
<ComponentRef Id="cmpD5E6EA59DB565F052E0217CB3248DAE5"/>
</PatchFamily>
</Fragment>
</Wix>
ComponentRef Id's refers to component fragments create by heat.exe harvest of projects mentioned earlier.
Any idea, what could be a problem and why transform doesn't contain any changes?

I think this may be a bug in 3.6. I tried for several hours to get this to work with version 3.6.3303.1, but I always got the PYRO1079 error. I finally downgraded to version 3.5.2519.0, and the error has not reoccurred.
I had to give up my MediaTemplate node and the Directory attribute of my ComponentGroup node after downgrading. I don't know if this was part of the solution or not (i.e. this might have fixed the problem instead of the downgrade).

Related

Wix Toolset - Variable Shared Across Projects/Solution

I am trying to share a variable across 2 of my wix projects but I am having issues.
Basically I am trying to accomplish having the version number of my bootstrapper and MSI in one file and then this referenced by the two projects.
I have three projects
Install - This is a setup project that creates an .msi file
Bootstrapper - This is a Wix Bootstrapper project that references and runs the .msi file at runtime
Shared - This is a wixlib project that contains a single variable in a fragment that is the version number
The shared project contains a single file i have called GlobalVars.wxs and looks like this
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<?define VersionNo = "6.86.123"?>
</Fragment>
</Wix>
The bootstrapper references this variable like this
<Bundle Name="ProgramName" Version="$(var.VersionNo)" Manufacturer="CompanyName" UpgradeCode="Guid" Compressed="no">
and the Install project references the variable like this - and has a reference to the .wxs from the shared project
<Product Id="*" Name="Program Name" Language="2057" Version="$(var.VersionNo)" Manufacturer="CompanyName" UpgradeCode="guid">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" InstallPrivileges="elevated"/>
<?include GlobalVars.wxs ?>
Both projects have references setup to the wixlib project that contains the variable
When i attempt to build I am getting this error on both the install and bootstrapper project
Undefined preprocessor variable '$(var.VersionNo)'.
If the <?include?> tag resolved the issue I would expect the install project to build
Does anyone have any ideas as to what I might be doing wrong here?
To me it looks like the variable has not been defined by the time the build attempts to call it, but I am unsure as to show to change the order to ensure the variable is defined before anything else
Thanks for the help
I believe the answer to this question will help. I've used it and noticed that properties seem to be usable in my main wxs file.
To summarise, you need to set up a fake componentGroup in your library fragment, and use it in your installer. You do not need the include anymore, as long as the fake componentGroup from your fragment is referenced as a componentGroupRef in your main install, and your wixlib project is referenced in your installer project through VS (you said you'd already done this in your comments above).
Your library fragment might look something like this.
<Fragment id="fragment_id_may_not_be_needed">
<?define VersionNo = "6.86.123"?>
<ComponentGroup Id="c.define_version_num" />
</Fragment>
If the define for whatever reason doesn't work, try using a property instead. I'd be interested to know which works. Properties seem to work for me.
Then reference it in your main install like this:
<Feature Id="Main_installation" Title="Main installation" Level="1">
<!-- bringing in fragments from the shared libraries -->
<ComponentGroupRef Id="c.define_version_num" />
</feature>
Give it a whirl.

Wix can't detect changes

This is my component:
<Component Id="ServiceComponent" Directory="INSTALLFOLDER">
<File Id="ServiceExecutable"
Name="$(var.LoggerService.TargetFileName)"
Source="$(var.LoggerService.TargetPath)"
Vital="yes"
KeyPath="yes"
DiskId="1" />
...
This is my patch:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
<Patch AllowRemoval='yes' Manufacturer='Test Ltd.' MoreInfoURL='www.test.com'
DisplayName='Test Patch' Description='Small Update Patch' Classification='Update'>
<Media Id='5000' Cabinet='Patch.cab'>
<PatchBaseline Id='Sample' />
</Media>
<PatchFamilyRef Id="SamplePatchFamily"/>
</Patch>
<Fragment>
<PatchFamily Id='SamplePatchFamily' Version='1.0.0.0' Supersede='yes'>
<ComponentRef Id='ServiceComponent' />
</PatchFamily>
</Fragment>
</Wix>
This is my patch generator script:
torch.exe -p -xi bin\Debug\1.1\SetupLogger.wixpdb bin\Debug\1.2\SetupLogger.wixpdb -out diff.wixmst
candle.exe patch.wxs
light.exe patch.wixobj -out patch.wixmsp
pyro.exe patch.wixmsp -out patch.msp -t Sample diff.wixmst
If I modify the file ServiceExecutable in the same path $(var.LoggerService.TargetPath) then Wix doesn't detect any change and saying to me:
warning PYRO1079 : The cabinet 'Patch.cab' does not contain any files. If this patch contains no files, this warning can likely be safely ignored. Otherwise, try passing -p to torch.exe when first building the transforms, or add a ComponentRef to your PatchFamily authoring to pull changed files into the cabinet.
diff.wixmst : error PYRO0227 : The transform being built did not contain any differences so it could not be created.
However, if I modify the file ServiceExecutable and put it in the new dir, then specify that new dir in the File source tag, then the change is picked up by Wix and everything is fine.
Question: how can I make Wix to detect the change without changing source path? I think Wix should be able to detect change because the checksum of the KeyPath file has been changed.

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: Patch installer superseding previous versions (1.0.0 -> 1.0.1, 1.0.0 -> 1.0.2, 1.0.1 -> 1.0.2, aso.)

I am trying to provide a simple installer package (MSI) which I want to support with updates (patches) that supersede all previous patches. So I have a MSI V1.0.0 and 2 patches V1.0.1 and V1.0.2. The user should be able to just install the latest patch regardless which previous patches were already applied to the system. My project contains 2 features (Client and Server). The basis of the patch so is always the RTM package (HelloWorld 1.0.msi / HelloWorld 1.0.wixpdb).
The generation (build) of all patches work, so the update procedures 1.0.0 -> 1.0.1 and 1.0.0 -> 1.0.2 do, BUT when i try to update from 1.0.1 to 1.0.2 the patch fails with the following error message: "The upgrade patch cannot be installed by the Windows Installer service because the program to be upgraded may be missing, or the upgrade patch may update a different version of the program. Verify that the program to be upgraded exists on your computer and that you have the correct upgrade patch.". Even worse, when I run the 1.0.1 patch on a system where 1.0.2 is already installed, the patch overwrites the existing installation with an older version!? I am totally confused...
I also found several blog entries on the web about patching, but nothing that works with my supersede szenario.
wix patching code - "patch1.wxs":
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Patch
AllowRemoval="yes"
Manufacturer="My Company"
MoreInfoURL="http://www.mycompany.com/"
DisplayName="HelloWorld V1.0 Patch 1"
Description="Patch intaller updating HelloWorld V1.0.x to V1.0.1"
Classification="Update">
<Media Id="32000" Cabinet="HelloWorldRTM.cab">
<PatchBaseline Id="HelloWorldRTM">
<Validate ProductId="yes" UpgradeCode="yes" ProductVersionOperator="LesserOrEqual" />
</PatchBaseline>
</Media>
<PatchFamilyRef Id="HelloWorldPatchFamily"/>
</Patch>
<Fragment>
<PatchFamily Id='HelloWorldPatchFamily' Version='1.0.1.0' Supersede='yes'>
<PropertyRef Id="ProductVersion"/>
<ComponentRef Id="HelloWorldServer.dll"/>
</PatchFamily>
</Fragment>
</Wix>
patch 1 build script - "generate_patch1.bat":
"%WIX%\bin\torch.exe" -p -xi ".\_Distrb\HelloWorld 1.0.wixpdb" ".\_Distrb\HelloWorld 1.0.1.wixpdb" -out ".\_Build\patch1.wixmst"
"%WIX%\bin\candle.exe" -out ".\_Build\patch1.wixobj" ".\patch1.wxs"
"%WIX%\bin\light.exe" ".\_Build\patch1.wixobj" -out ".\_Build\patch1.wixmsp"
"%WIX%\bin\pyro.exe" ".\_Build\patch1.wixmsp" -out ".\_Distrb\HelloWorld 1.0 Patch1.msp" -t HelloWorldRTM ".\_Build\patch1.wixmst"
wix patching code - "patch2.wxs":
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Patch
AllowRemoval="yes"
Manufacturer="My Company"
MoreInfoURL="http://www.mycompany.com/"
DisplayName="HelloWorld V1.0 Patch 2"
Description="Patch intaller updating HelloWorld V1.0.x to V1.0.2"
Classification="Update">
<Media Id="32000" Cabinet="HelloWorldRTM.cab">
<PatchBaseline Id="HelloWorldRTM">
<Validate ProductId="yes" UpgradeCode="yes" ProductVersionOperator="LesserOrEqual" />
</PatchBaseline>
</Media>
<PatchFamilyRef Id="HelloWorldPatchFamily"/>
</Patch>
<Fragment>
<PatchFamily Id='HelloWorldPatchFamily' Version='1.0.2.0' Supersede='yes'>
<PropertyRef Id="ProductVersion"/>
<ComponentRef Id="HelloWorldServer.dll"/>
<ComponentRef Id="HelloWorld.exe"/>
</PatchFamily>
</Fragment>
</Wix>
patch 2 build script - "generate_patch2.bat":
"%WIX%\bin\torch.exe" -p -xi ".\_Distrb\HelloWorld 1.0.wixpdb" ".\_Distrb\HelloWorld 1.0.2.wixpdb" -out ".\_Build\patch2.wixmst"
"%WIX%\bin\candle.exe" -out ".\_Build\patch2.wixobj" ".\patch2.wxs"
"%WIX%\bin\light.exe" ".\_Build\patch2.wixobj" -out ".\_Build\patch2.wixmsp"
"%WIX%\bin\pyro.exe" ".\_Build\patch2.wixmsp" -out ".\_Distrb\HelloWorld 1.0 Patch 2.msp" -t HelloWorldRTM ".\_Build\patch2.wixmst"
I had a similar problem, and got it fixed by adding the appropriate validation to the patch wxs. Try this...
<Media Id="32000" Cabinet="HelloWorldRTM.cab">
<PatchBaseline Id="HelloWorldRTM">
<Validate ProductId="yes" UpgradeCode="yes" ProductVersion="Major" ProductVersionOperator="GreaterOrEqual" />
</PatchBaseline>
</Media>
If you don't want the patch to work when the installed revision number is greater than the patch's revision number, you might want to change the ProductVersion to "Update" and set the ProductVersion to "GreaterOrEqual".
I hope it works for you!

WIX - Is it possible to create a language neutral patch for multiple MSI?

We have the multiple MSI's for the different languages, so each MSI has its own ProductCode and UpgradeCode. Using English MSI's, we created a patch using Aaron's approach in http://blogs.msdn.com/astebner/archive/2007/10/26/5700191.aspx, e.g. candle / light / torch / pyro
with the following Patch.wxs:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Patch
AllowRemoval="yes"
Manufacturer="xxx"
MoreInfoURL="xxx"
DisplayName="MyProduct First Patch"
Description="My Product First Patch"
Classification="Update Rollup"
>
<Media Id="5000" Cabinet="RTM.cab" >
<PatchBaseline Id="RTM"/>
</Media>
<PatchFamilyRef Id="PatchFamilyRollup"/>
</Patch>
<Fragment>
<PatchFamily Id='PatchFamilyRollup' Version='1.1.1.1' Supersede='yes'>
...
However, when we apply this patch on the machine where non-English MSI was installed, we get the following error:
"The upgrade patch cannot be installed by the Windows Installer service because the program to be upgraded may be missing, or the upgrade patch may update a different version of the program. Verify that the program to be upgraded exists on your computer and that you have the correct upgrade patch."
So my question is,
is it possible to create a patch (MSP) that can be used on any language?
If so, what needs to be done?
I think you should experiment with the Validate element, which is a child of PatchBaseline, and the validation flags of torch.exe command line. The right combination of bits will let you install your patch.
Thanks Yan for giving me a right direction. I played with "Validate" element and "torch" command quite a few hours, but I got the same error. Then my co-worker showed me "TargetProductCode" element. After a few trials, I finally made it work although the solution isn't purely language-neutral. The answer I found is a combination of "Validate" element and "TargetProductCode" element. I am posting my own answer so that someone may get benefit out of it.
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Patch
AllowRemoval="yes"
Manufacturer="xxx"
MoreInfoURL="xxx"
DisplayName="MyProduct First Patch"
Description="My Product First Patch"
Classification="Update Rollup"
>
<Media Id="5000" Cabinet="RTM.cab">
<PatchBaseline Id="RTM" >
<Validate ProductId='no' ProductLanguage='no' ProductVersionOperator='LesserOrEqual' UpgradeCode='no' />
</PatchBaseline>
</Media>
<TargetProductCodes Replace='yes'>
<!-- list all language specific ProductCode here. -->
<TargetProductCode Id='{xxxxx}' /> <!-- ProductCode for English -->
<TargetProductCode Id='{yyyyy}' /> <!-- ProductCode for French -->
</TargetProductCodes>
<PatchFamilyRef Id="PatchFamilyRollup"/>
</Patch>
...