WMI - выбор из Win32_Product занимает много времени

Я перечисляю установленные приложения, используя 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);
    }

ВЫБРАТЬ * ИЗ 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, ГДЕ НАМ НРАВИТСЯ "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

6 ответов

Решение

WMI не торопится, как вы уже заметили. Итерация по реестру может помочь вам.

Вы можете взглянуть на Получить установленные приложения в системе здесь на stackru, где упоминаются оба метода.

Если вы запросите "Win32_product", msi-installer проверяет и проверяет каждый продукт.

Статья базы знаний http://support.microsoft.com/kb/974524 показывает:

Класс Win32_product не оптимизирован для запросов. Такие запросы, как "выберите * из Win32_Product, где (например," Sniffer% ")", требуют, чтобы WMI использовал поставщика MSI для перечисления всех установленных продуктов, а затем последовательно анализировал полный список для обработки предложения "где". Этот процесс также инициирует проверку согласованности установленных пакетов, проверку и исправление установки. С учетной записью, имеющей только пользовательские привилегии, так как учетная запись пользователя может не иметь доступа к нескольким местоположениям, может вызвать задержку запуска приложения и событие 11708, сообщающее об ошибке установки.

Win32reg_AddRemovePrograms - намного более легкий и эффективный способ сделать это, который избегает вызовов для проверки устойчивости, особенно в закрытой среде. Поэтому при использовании Win32reg_AddRemovePrograms мы не будем вызывать msiprov.dll и не будем инициировать проверку устойчивости.

Так что будьте осторожны с "Win32_product".

Обновление: хорошая статья https://sdmsoftware.com/group-policy-blog/wmi/why-win32_product-is-bad-news/

Как упомянуто здесь, Реестр ненадежен, а 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;
}

Как указывает Бернхард, использование WMI Win32_Product инициирует проверку целостности состояния пакета и, следовательно, будет довольно медленным в использовании - и в особых случаях может вызвать самовосстановление MSI (я никогда не видел, чтобы это происходило на моих машинах),

Вместо WMI можно напрямую использовать интерфейс автоматизации MSI для перечисления приложений, установленных с помощью пакетов установщика Windows (файлы MSI) на компьютере. Это очень быстро и вообще не касается WMI.

Посмотрите этот пример: как узнать, какие продукты установлены - на более новый продукт уже установлены окна MSI (полнофункциональный, но простой и понятный пример VBScript - ознакомьтесь с ним). Есть много свойств, которые вы можете получить для каждого продукта, пожалуйста, обратитесь к документации MSDN для интерфейса автоматизации MSI. Надеюсь, что связанный пример кода VBScript и документация MSDN, взятые вместе, должны помочь вам быстро начать работу.

PS: я знаю, что это старый вопрос, но эта проблема продолжает появляться (особенно медлительность WMI) - просто для дальнейшего использования.

Вы должны использовать SELECT Name FROM Win32_Product в WMI Query у меня работает

SELECT * сделать загрузку всех членов данных, поэтому его использование занимает много времени

Powershell 5.1 вместо этого имеет «get-package».

      get-package *chrome*

Name                           Version          Source                 ProviderName
----                           -------          ------                 ------------
Google Chrome                  109.0.5414.75                           msi
Другие вопросы по тегам