Now, given a certain .msu file and my local computer, is there a way to iterate over those rules and find out if one is not satisfied - and which one?
Can I use WSUS 3.0 Class Library for this purpose? Or is there a tool / script?
You can Update Applicability Rules via the WSUS 3.0 Class Library though it doesn't offer functionality to check if the rules will pass, unless (I guess) you run the installer but that doesn't tell you which one failed.
Simon mentioned the WUAPI library doesn't expose the inner rules and (afaik) there is no way to match the WUAPI ResultCodes to the ApplicabilityRules that fail.
And unfortunately librarys like Microsoft.Deployment.WindowsInstaller.dll
dont work with MSU files so we're out of luck with the "off-the-shelf" options. Therefore you have to do it manually with code and the (msu.xml) XML file:
<Updates>
<UpdateIdentity UpdateID="E6CF1350-C01B-414D-A61F-263D14D133B4" RevisionNumber="1" />
<Properties UpdateType="Category" />
<ApplicabilityRules>
<IsInstalled>
<True />
</IsInstalled>
</ApplicabilityRules>
<UpdateIdentity UpdateID="2bf7ed9c-6f43-493a-b156-db20f08c44c4" RevisionNumber="101" />
<Properties UpdateType="Detectoid" />
<Relationships />
<ApplicabilityRules>
<IsInstalled>
<b.RegSz Key="HKEY_LOCAL_MACHINE" Subkey="SYSTEM\CurrentControlSet\Control\Nls\Language" Value="InstallLanguage" Comparison="EqualTo" Data="0409" />
</IsInstalled>
</ApplicabilityRules>
<UpdateIdentity UpdateID="6AECE9A4-19E3-4BC7-A20C-070A5E31AFF4" RevisionNumber="100" />
<Properties UpdateType="Detectoid" />
<Relationships></Relationships>
<UpdateIdentity UpdateID="3B4B8621-726E-43A6-B43B-37D07EC7019F" />
<ApplicabilityRules>
<IsInstalled>
<b.WmiQuery Namespace="root\cimv2" WqlQuery="SELECT Manufacturer FROM Win32_ComputerSystem WHERE Manufacturer = 'Dell Inc.' or Manufacturer = 'Samsung Electronics' or Manufacturer = 'Hewlett-Packard' or Manufacturer = 'Gateway'" />
</IsInstalled>
</ApplicabilityRules>
</Updates>
Use this code to see which ApplicabilityRules fail:
private void btnWillPassApplicabilityRules_Click(object sender, EventArgs e)
{
XDocument doc = XDocument.Load("msu.xml");
var elements = doc.Element("Updates").Elements("ApplicabilityRules").Elements("IsInstalled").Elements();
foreach (var element in elements) {
if (element.ToString().StartsWith("<b.RegSz")) {
string subKeyName = element.Attribute("Subkey").Value;
string keyName = element.Attribute("Value").Value;
string keyValue = element.Attribute("Data").Value;
//TODO: Leave the Registry Hive "Switch()" upto reader to fully implement
if (!ValueExistsInRegistry(Registry.LocalMachine, subKeyName, keyName, keyValue)) {
Console.WriteLine("Install is not applicable as Applicability Rule failed: " + element.ToString());
}
}
else if (element.ToString().StartsWith("<b.WmiQuery")) {
string nameSpace = element.Attribute("Namespace").Value;
string wqlQuery = element.Attribute("WqlQuery").Value;
if (!ValueExistsInWMI(nameSpace, wqlQuery)) {
Console.WriteLine("Install is not applicable as Applicability Rule failed: " + element.ToString());
}
}
}
}
private bool ValueExistsInRegistry(RegistryKey root, string subKeyName, string keyName, string keyValue)
{
using (RegistryKey key = root.OpenSubKey(subKeyName)) {
if (key != null) return keyValue == key.GetValue(keyName).ToString();
}
return false;
}
private bool ValueExistsInWMI(string nameSpace, string wqlQuery)
{
ManagementScope scope = new ManagementScope(String.Format("\\\\{0}\\" + nameSpace, "."), null); //The "." is for your local PC
scope.Connect();
ObjectQuery query = new ObjectQuery(wqlQuery);
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
if (searcher.Get().Count == 0) {
return false;
}
else {
return true;
}
return false;
}
}
Before you run the Applicability Rules its best to first check if the update will pass the Operating System (OS) and Service Pack (SP) Applicability test. There is no point checking registry/wmi etc to determine if an upgrade will pass the rules if its not applicable to the OS and SP.
To see the ApplicabilityInfo, run the expand command line utility:
expand -f:* "C:\temp\msu\Windows6.1-KB2973201-x64.msu" "C:\temp\msu"
This will create the following files:
- WSUSSCAN.cab
- Windows6.1-KB2973201-x64.cab
- Windows6.1-KB2973201-x64.xml
- Windows6.1-KB2973201-x64-pkgProperties.txt
The xml and txt files take about 5 seconds to be created. Open the pkgProperties.txt file and the top line has the info:
ApplicabilityInfo="Windows 7.0 Client SP1;Windows 7.0 Server Core
SP1;Windows 7.0 Embedded SP1;Windows 7.0 Server SP1;Windows 7.0 WinPE
3.1;"
MSDN Ref: Description of the Windows Update Standalone Installer in Windows