如何以编程方式检查Windows更新的适用性规则?

通过浏览Windows更新.msu文件的内容(例如,使用诸如7zip的工具),可以find一系列定义先决条件和适用性规则的文件。 例如:

<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> ... <UpdateIdentity UpdateID="3B4B8621-726E-43A6-B43B-37D07EC7019F" /><ApplicabilityRules><IsInstalled><b.WmiQuery Namespace="root\cimv2" WqlQuery="SELECT Manufacturer FROM Win32_ComputerSystem WHERE Manufacturer = 'Samsung Electronics' or Manufacturer = 'Hewlett-Packard' or Manufacturer = 'Gateway'" /></IsInstalled></ApplicabilityRules> ... 

现在,给定一个.msu文件和我的本地计算机,是否有办法迭代这些规则,并找出是否不满意 – 哪一个?

我可以为此使用WSUS 3.0类库吗? 还是有一个工具/脚本?

我真正想要的是准确地知道什么情况使计算机拒绝某个Windows更新(KB2973201),并显示消息更新不适用于您的计算机 (此错误代码为WU_E_NOT_APPLICABLE )。

关于这些更新的适用性规则似乎太less了。 有没有好的来源?

参考文献:

  • Windows更新代理如何确定更新的状态
  • msdn上有一些WSUS的东西
  • Windows更新错误代码列表

现在,给定一个.msu文件和我的本地计算机,是否有办法迭代这些规则,并找出是否不满意 – 哪一个?
我可以为此使用WSUS 3.0类库吗? 还是有一个工具/脚本?

您可以通过WSUS 3.0类库更新适用性规则 ,虽然它不提供检查规则是否会通过的功能,除非(我猜)您运行安装程序,但并不告诉您哪个失败。

Simon提到,WUAPI库不公开内部规则,(afaik)没有办法将WUAPI结果代码与失败的适用性规则相匹配。

不幸的是像Microsoft.Deployment.WindowsInstaller.dll图书馆不工作与MSU文件,所以我们运气不好“现成的”选项。 因此,您必须使用代码和(msu.xml)XML文件手动执行此操作

 <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> 

使用此代码查看哪些ApplicabilityRules失败:

 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; } } 

在运行适用性规则之前,最好先检查更新是否会通过操作系统(OS)和Service Pack(SP)适用性测试。 如果升级程序不适用于OS和SP,那么检查注册表/ wmi等是没有意义的

要查看ApplicabilityInfo,请运行扩展命令行实用程序:

 expand -f:* "C:\temp\msu\Windows6.1-KB2973201-x64.msu" "C:\temp\msu" 

这将创建以下文件:

  • WSUSSCAN.cab
  • Windows6.1-KB2973201-x64.cab
  • Windows6.1-KB2973201-x64.xml
  • Windows6.1-KB2973201-x64的pkgProperties.txt

xml和txt文件大约需要5秒钟才能创建。 打开pkgProperties.txt文件,顶行有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参考: Windows中的Windows更新独立安装程序的说明

您可以使用Windows更新代理API来查询已安装的更新(它实际上有很多信息),如下所示:

  // in .NET, you need to add a reference // to the WUAPI COM component located in \windows\system32\wuapi.dll // to be able to access the WUAPI object model UpdateSearcher searcher = new UpdateSearcher(); searcher.Online = false; // you can remove this line if you allow the API to get online to search var res = searcher.Search("IsInstalled=0"); // search not installed update foreach (IUpdate update in res.Updates) { Console.WriteLine("update:" + update.Title); // get history information // this can return nothing for example it it was hidden by end user // note we use update's identity and rev number here for matching a specific update var histories = searcher.QueryHistory(0, searcher.GetTotalHistoryCount()).OfType<IUpdateHistoryEntry>().Where( h => h.UpdateIdentity.UpdateID == update.Identity.UpdateID && h.UpdateIdentity.RevisionNumber == update.Identity.RevisionNumber); foreach (var history in histories) { Console.WriteLine(" code:" + history.ResultCode); Console.WriteLine(" hr:0x" + history.HResult.ToString("X8")); } } 

但是,这不会告诉你什么是内部规则(注册表/ wmi等)来确定是否安装了updagres。 这不是由WUAPI公开的。