WMI - 从Win32_Product中选择需要很长时间

4

我正在使用 WMI 枚举已安装的应用程序,但无论如何重构此块代码,它都需要相对较长的时间才能完成。在我的环境中每次需要 13 秒。是否有更好(更快)的方法来检查一个程序是否已经安装?(我以 iTunes 作为检查的示例程序)

    private static string Timestamp
    {
        get { return DateTime.Now.ToString("HH:mm:ss.ffff"); }
    }

    private static void LoadInstalledPrograms()
    {
        List<string> installedPrograms = new List<string>();
        Console.WriteLine("0 - {0}", Timestamp);
        ManagementObjectSearcher mos = new ManagementObjectSearcher("SELECT * FROM Win32_Product");
        Console.WriteLine("1 - {0}", Timestamp);
        ManagementObjectCollection managementObjectCollection = mos.Get();
        Console.WriteLine("2 - {0}", Timestamp);
        foreach (ManagementObject mo in managementObjectCollection)
        {
            installedPrograms.Add(mo["Name"].ToString());
        }
        Console.WriteLine("3 - {0}", Timestamp);
        Console.WriteLine("Length - {0}", installedPrograms.Count);
    }

SELECT * FROM Win32_Product

0 - 08:08:51.3762
1 - 08:08:51.3942
2 - 08:08:51.4012
3 - 08:09:04.8326
Length - 300

SELECT * FROM Win32_Product WHERE name = 'iTunes'

0 - 08:14:17.6529
1 - 08:14:17.6709
2 - 08:14:17.6779
3 - 08:14:31.0332
Length - 1

选择 * 从 Win32_Product 中,其中 name LIKE 'iTunes'

0 - 08:16:38.2719
1 - 08:16:38.2899
2 - 08:16:38.2999
3 - 08:16:51.5113
Length - 1

从 Win32_Product 中选择名称为“iTunes”的产品

0 - 08:19:53.9144
1 - 08:19:53.9324
2 - 08:19:53.9394
3 - 08:20:07.2794
Length - 1

1
使用Win32Reg_AddRemovePrograms,它更快。 - Cole9350
需要安装SMS服务器2003才能使用Win32Reg_AddRemovePrograms,这是我无法强制执行的要求。 - invertigo
6个回答

6

如果您查询“Win32_product”,msi-installer会检查和验证每个产品。

KB文章http://support.microsoft.com/kb/974524显示:

Win32_product类不是查询优化的。查询,如“select * from Win32_Product where (name like 'Sniffer%')”,需要WMI使用MSI提供程序枚举所有安装的产品,然后顺序解析完整列表以处理“where”子句。此过程还启动了包安装的一致性检查,以验证和修复安装。在仅具用户权限的帐户中,由于用户帐户可能无法访问很多位置,可能会导致应用程序启动延迟并导致事件11708表示安装失败。

Win32reg_AddRemovePrograms是一个更轻便和有效的方法来执行这个操作,它避免了对弹性检查的调用,特别是在锁定的环境中。因此,在使用Win32reg_AddRemovePrograms时,我们将不会调用msiprov.dll,也不会启动弹性检查。

因此,请小心使用“Win32_product”。

更新:好的文章https://sdmsoftware.com/group-policy-blog/wmi/why-win32_product-is-bad-news/


是的,我已经添加了另一个答案,其中包含有关如何使用MSI自动化接口枚举安装的MSI文件的信息。我只想提到,使用WMI的Win32_Product确实会启动软件包状态检查,但仅在发现不一致性时才会触发自修复。我从未在我的任何计算机上遇到过这种情况,但它确实可能发生。但总体而言,WMI非常缓慢。它很有用,但如果可以的话,请使用其他工具。 - Stein Åsmul
使用 WMI Win32_Product 的替代方案是什么? - Kiquenet

3

您已经注意到WMI需要耗费一定的时间。通过迭代注册表可能会对您有所帮助。

您可以查看stackoverflow上的获取系统中安装的应用程序,其中提到了这两种方法。


遍历注册表的速度要快得多,尽管它有自己的细微差别,我需要仔细研究一下,看看它是否是一个可行的选项。 - invertigo
这最终成为了最佳解决方案,因为它 a) 更快,b) 从每个查询的应用程序的 win32_product 日志和事件查看器条目进行选择,这是我的应用程序不希望出现的副作用。 - invertigo

2

此处所述,注册表不可靠且WMI速度较慢。因此,对我而言,最好的选择是使用Windows Installer API。将msi.dll添加到您的引用中,然后根据您的需求调整以下代码:

public static string GetVersionOfInstalledApplication(string queryName)
{
    string name;
    string version;
    Type type = Type.GetTypeFromProgID("WindowsInstaller.Installer");
    Installer installer = Activator.CreateInstance(type) as Installer;
    StringList products = installer.Products;
    foreach (string productGuid in products)
    {
        string currName = installer.ProductInfo[productGuid, "ProductName"];
        string currVersion = installer.ProductInfo[productGuid, "VersionString"];
        if (currName == queryName)
        {
            name = currName;
            version = currVersion;
            return version;
        }
    }
    return null;
}

2
如Bernhard所指出的,WMI使用Win32_Product会启动程序包状态的完整性检查,因此使用它会非常缓慢,并且在特殊情况下可能会触发MSI自我修复操作(我从未在我的机器上看到这种情况发生)。
您可以直接使用MSI自动化界面枚举通过Windows Installer程序包(MSI文件)安装在计算机上的应用程序,而无需使用WMI。这非常快速且完全不需要触及WMI。
请参考此示例全面而基本易懂的VBScript示例 - 一定要查看。每个产品都可以检索许多属性,请查阅MSI自动化界面的MSDN文档。联合链接的VBScript示例代码和MSDN文档应该能够帮助您快速入门。
附注:我知道这是一个旧问题,但这个问题一直存在(尤其是WMI的速度缓慢),仅供参考。

1

在WMI查询中,您应该使用SELECT Name FROM Win32_Product,这对我有效。

SELECT *会加载所有数据成员,因此使用它会花费很多时间。


这似乎没有帮助。 - js2010

0

Powershell 5.1有“get-package”。

get-package *chrome*

Name                           Version          Source                 ProviderName
----                           -------          ------                 ------------
Google Chrome                  109.0.5414.75                           msi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接