Our WiX installer deploys a .NET 4.0 WinForms application to Windows Vista and 7 desktops. The application includes a Portable Class Library that requires a .NET patch (KB24
I achieved this with WiX Burn using the following fragment (using the product codes from @KMoraz):
<!-- KB2468871 update to support portable class libraries (PCL) on XP -->
<Fragment>
<!-- codes from http://stackoverflow.com/a/9506530 -->
<util:ProductSearch ProductCode="{0A0CADCF-78DA-33C4-A350-CD51849B9702}"
Variable="KB2468871_NET32_Installed" />
<util:ProductSearch ProductCode="{8E34682C-8118-31F1-BC4C-98CD9675E1C2}"
Variable="KB2468871_NET64_Installed" />
<PackageGroup Id="NDP40_KB2468871_v2">
<ExePackage Id="NDP40_KB2468871_v2_x86" Compressed="no" Vital="yes" Permanent="yes"
SourceFile="NDP40-KB2468871-v2-x86.exe"
DownloadUrl="http://download.microsoft.com/download/2/B/F/2BF4D7D1-E781-4EE0-9E4F-FDD44A2F8934/NDP40-KB2468871-v2-x86.exe"
DetectCondition="KB2468871_NET32_Installed"
InstallCondition="NOT VersionNT64"
InstallCommand="/q"/>
<ExePackage Id="NDP40_KB2468871_v2_x64" Compressed="no" Vital="yes" Permanent="yes"
SourceFile="NDP40-KB2468871-v2-x86.exe"
DownloadUrl="http://download.microsoft.com/download/2/B/F/2BF4D7D1-E781-4EE0-9E4F-FDD44A2F8934/NDP40-KB2468871-v2-x64.exe"
DetectCondition="KB2468871_NET64_Installed"
InstallCondition="VersionNT64"
InstallCommand="/q"/>
</PackageGroup>
</Fragment>
The util
namespace refers to the Wix Util Extension. You'll need a xmlns:util="http://schemas.microsoft.com/wix/UtilExtension"
in your root element and a reference to WixUtilExtension.dll
.
The installers will be automatically downloaded if required. You need to have the installer files saved locally in the root of your WiX project with the same name as specified by SourceFile
for it to build, but they won't be added to the setup bundle because Compressed
is set to no
.
I've been playing around with a similar situation and found @KMoraz 's answer to be almost right. (NOTE: @KMoraz, I would leave this as a comment, but don't have the rep yet!)
The Microsoft.Deployment.WindowsInstaller
namespace appears to be the way to go, but filtering by product code may be too restrictive. I ran into a problem where the patch was installed on a machine, but not associated with either product code {F5B09CFD-F0B2-36AF-8DF4-1DF6B63FC7B4}
or {8E34682C-8118-31F1-BC4C-98CD9675E1C2}
.
My solution was to instead use PatchInstallation.AllPatches
, which returns an unfiltered list of installed patches on the system, then look for the answer using linq.
public static bool CheckForPatch()
{
return IsPatchAlreadyInstalled("KB2468871")
}
public static bool IsPatchAlreadyInstalled(string patchCode)
{
var patches = PatchInstallation.AllPatches.ToList();
patches.ForEach(x => Console.WriteLine("--found patch {0} for {1}",x.DisplayName,x.ProductCode));
return patches.Any(patch => patch.DisplayName == patchCode);
}
Sample output:
--found patch Microsoft Office 2010 Service Pack 1 (SP1) for {90140000-001F-0409-0000-0000000FF1CE}
--found patch Update for Microsoft Office 2010 (KB2553270) 32-Bit Edition for {90140000-001F-0409-0000-0000000FF1CE}
--found patch Microsoft Office 2010 Service Pack 1 (SP1) for {90140000-001F-0C0A-0000-0000000FF1CE}
--found patch Update for Microsoft Office 2010 (KB2553270) 32-Bit Edition for {90140000-001F-0C0A-0000-0000000FF1CE}
--found patch Microsoft Office 2010 Service Pack 1 (SP1) for {90140000-001F-040C-0000-0000000FF1CE}
--found patch Update for Microsoft Office 2010 (KB2553270) 32-Bit Edition for {90140000-001F-040C-0000-0000000FF1CE}
--found patch KB2533523 for {3C3901C5-3455-3E0A-A214-0B093A5070A6}
--found patch KB2518870 for {3C3901C5-3455-3E0A-A214-0B093A5070A6}
--found patch KB2656351 for {3C3901C5-3455-3E0A-A214-0B093A5070A6}
--found patch KB2633870 for {3C3901C5-3455-3E0A-A214-0B093A5070A6}
--found patch KB2468871 for {3C3901C5-3455-3E0A-A214-0B093A5070A6}
--found patch KB2572078 for {3C3901C5-3455-3E0A-A214-0B093A5070A6}
--found patch KB2533523 for {0A0CADCF-78DA-33C4-A350-CD51849B9702}
--found patch KB2656351 for {0A0CADCF-78DA-33C4-A350-CD51849B9702}
--found patch KB2468871 for {0A0CADCF-78DA-33C4-A350-CD51849B9702}
--found patch KB2487367 for {0A0CADCF-78DA-33C4-A350-CD51849B9702}
Win32_QuickFixEngineering won't return all updates. Actually, it returns only updates restricted to QFE:
Updates supplied by Microsoft Windows Installer (MSI) or the Windows update site (http://update.microsoft.com) are not returned by Win32_QuickFixEngineering.
The update you're after is an MSI patch. Use Microsoft.Deployment.WindowsInstaller
(aka DTF - Deployment Tools Foundation, part of the WiX toolset) to query the applied MSI patches:
public static bool IsPatchAlreadyInstalled(string productCode, string patchCode)
{
var patches =
PatchInstallation.GetPatches(null, productCode, null, UserContexts.Machine, PatchStates.Applied);
return patches.Any(patch => patch.DisplayName == patchCode);
}
In this case, KB2468871 is one of .NET Framework 4 updates. The following will return true if the updates have been applied on the machine:
IsPatchAlreadyInstalled("{F5B09CFD-F0B2-36AF-8DF4-1DF6B63FC7B4}", "KB2468871");// .NET Framework 4 Client Profile 64-bit
IsPatchAlreadyInstalled("{8E34682C-8118-31F1-BC4C-98CD9675E1C2}", "KB2468871");// .NET Framework 4 Extended 64-bit
IsPatchAlreadyInstalled("{3C3901C5-3455-3E0A-A214-0B093A5070A6}", "KB2468871");// .NET Framework 4 Client Profile 32-bit
IsPatchAlreadyInstalled("{0A0CADCF-78DA-33C4-A350-CD51849B9702}", "KB2468871");// .NET Framework 4 Extended 32-bit