Detect and require a Windows QFE/patch for during installation Detect and require a Windows QFE/patch for during installation windows windows

Detect and require a Windows QFE/patch for during installation


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-bitIsPatchAlreadyInstalled("{8E34682C-8118-31F1-BC4C-98CD9675E1C2}", "KB2468871");// .NET Framework 4 Extended 64-bitIsPatchAlreadyInstalled("{3C3901C5-3455-3E0A-A214-0B093A5070A6}", "KB2468871");// .NET Framework 4 Client Profile 32-bitIsPatchAlreadyInstalled("{0A0CADCF-78DA-33C4-A350-CD51849B9702}", "KB2468871");// .NET Framework 4 Extended 32-bit


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}