WMI - select from Win32_Product takes a long time
Asked Answered
D

6

4

I am enumerating installed applications using WMI, and this block is taking a relatively long time to complete no matter how I structure it. It takes 13 seconds in my environment every time. Is there a better (faster) way to check if a program is installed? (I'm using iTunes as an example program to check for)

    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

SELECT * FROM Win32_Product WHERE 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

SELECT name FROM Win32_Product WHERE name LIKE 'iTunes'

0 - 08:19:53.9144
1 - 08:19:53.9324
2 - 08:19:53.9394
3 - 08:20:07.2794
Length - 1
Dieppe answered 1/8, 2014 at 15:22 Comment(2)
Use Win32Reg_AddRemovePrograms, It's fasterDefunct
SMS server 2003 would have to be installed to use Win32Reg_AddRemovePrograms, a requirement I cannot enforce.Dieppe
T
3

WMI is taking it's time as you already noticed. Iterating through the registry might do the trick for you.

You might have a look at Get installed applications in a system here on stackoverflow, where both methods are mentioned.

Truman answered 1/8, 2014 at 15:47 Comment(2)
iterating through the registry is substantially faster, though it has its own nuances i'll have to work through to see if its a viable optionDieppe
this ended up being the best solution because it was a) faster and b) selecting from win32_product logs and entry to event viewer for each application that was queried, an undesirable side effect for my applicationDieppe
B
6

If you query "Win32_product" the msi-installer checks and validates every product.

The KB article http://support.microsoft.com/kb/974524 shows:

Win32_product Class is not query optimized. Queries such as “select * from Win32_Product where (name like 'Sniffer%')” require WMI to use the MSI provider to enumerate all of the installed products and then parse the full list sequentially to handle the “where” clause. This process also initiates a consistency check of packages installed, verifying and repairing the install. With an account with only user privileges, as the user account may not have access to quite a few locations, may cause delay in application launch and an event 11708 stating an installation failure.

Win32reg_AddRemovePrograms is a much lighter and effective way to do this, which avoids the calls to do a resiliency check, especially in a locked down environment. So when using Win32reg_AddRemovePrograms we will not be calling on msiprov.dll and will not be initiating a resiliency check.

So be careful with "Win32_product".

Update: nice article https://sdmsoftware.com/group-policy-blog/wmi/why-win32_product-is-bad-news/

Bradfordbradlee answered 20/9, 2017 at 7:37 Comment(2)
Yes, I have added another answer to this question with information on how to use the MSI automation interface to enumerate installed MSI files instead. I just want to mention that use of WMI's Win32_Product does indeed initiate a check of the package estate, but a self-repair is only triggered if an inconsistency is found. I have never had this happen on any of my boxes, but it can happen. But overall WMI is quite slow. It is useful, but use something else if you can.Adigranth
alternative to use WMI Win32_Product ?Cropdusting
T
3

WMI is taking it's time as you already noticed. Iterating through the registry might do the trick for you.

You might have a look at Get installed applications in a system here on stackoverflow, where both methods are mentioned.

Truman answered 1/8, 2014 at 15:47 Comment(2)
iterating through the registry is substantially faster, though it has its own nuances i'll have to work through to see if its a viable optionDieppe
this ended up being the best solution because it was a) faster and b) selecting from win32_product logs and entry to event viewer for each application that was queried, an undesirable side effect for my applicationDieppe
B
3

As Bernhard points out, WMI use of Win32_Product initiates an integrity check of the package estate, and will hence be quite slow to use - and in special cases it can trigger an MSI self-repair (I have never seen this happen on my machines).

Instead of WMI, you can use the MSI automation interface directly to enumerate the applications installed via Windows Installer packages (MSI files) on the machine. This is very quick and doesn't touch WMI at all.

See this example: how to find out which products are installed - newer product are already installed MSI windows (full blown, but basic and easy to understand VBScript example - do check it out). There are many properties you can retrieve for each product, please consult the MSDN documentation for the MSI automation interface. The linked sample VBScript code and the MSDN documentation taken together should help you get going quickly I hope.

P.S: I know this is an old question, but this issue keeps coming up (specifically the slowness of WMI) - just for future reference.

Brandibrandice answered 9/10, 2017 at 13:12 Comment(0)
B
2

As mentioned here Registry is not reliable and WMI is slow. Thus for me the best option was using Windows Installer API. Add msi.dll to your references and then adapt the following code to your needs:

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;
}
Battat answered 1/11, 2017 at 11:38 Comment(0)
R
1

You Should use SELECT Name FROM Win32_Product in WMI Query, it works for me

SELECT * make Load all Data Members, so using it are taking much time

Rudd answered 18/8, 2014 at 10:5 Comment(1)
This didn't seem to help.Beauvais
B
0

Powershell 5.1 has "get-package" instead.

get-package *chrome*

Name                           Version          Source                 ProviderName
----                           -------          ------                 ------------
Google Chrome                  109.0.5414.75                           msi
Beauvais answered 19/1, 2023 at 2:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.