4

How do I get a list of software products which are installed on the system. My goal is to iterate through these, and get the installation path of a few of them.

PSEUDOCODE ( combining multiple languages :) )

foreach InstalledSoftwareProduct
    if InstalledSoftwareProduct.DisplayName LIKE *Visual Studio*
        print InstalledSoftwareProduct.Path
Kiquenet
  • 14,494
  • 35
  • 148
  • 243
esac
  • 24,099
  • 38
  • 122
  • 179
  • 1
    I seriously doubt that you can do this on a Windows system. Most programs offer their own installer, leaving no standard as to how to detect if software is installed. – alternative Aug 19 '10 at 21:40
  • @mathepic: actually the vast majority of programs offer an installer that is one shell or another over an .msi installation (aka. Windows Installer). – Remus Rusanu Aug 19 '10 at 21:51

4 Answers4

14

You can use MSI api functions to enumerate all installed products. Below you will find sample code which does that.

In my code I first enumerate all products, get the product name and if it contains the string "Visual Studio" I check for the InstallLocation property. However, this property is not always set. I don't know for certain whether this is not the right property to check for or whether there is another property that always contains the target directory. Maybe the information retrieved from the InstallLocation property is sufficient for you?

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

class Program
{
    [DllImport("msi.dll", CharSet = CharSet.Unicode)]
    static extern Int32 MsiGetProductInfo(string product, string property,
        [Out] StringBuilder valueBuf, ref Int32 len);

    [DllImport("msi.dll", SetLastError = true)]
    static extern int MsiEnumProducts(int iProductIndex, 
        StringBuilder lpProductBuf);

    static void Main(string[] args)
    {
        StringBuilder sbProductCode = new StringBuilder(39);
        int iIdx = 0;
        while (
            0 == MsiEnumProducts(iIdx++, sbProductCode))
        {
            Int32 productNameLen = 512;
            StringBuilder sbProductName = new StringBuilder(productNameLen);

            MsiGetProductInfo(sbProductCode.ToString(),
                "ProductName", sbProductName, ref productNameLen);

            if (sbProductName.ToString().Contains("Visual Studio"))
            {
                Int32 installDirLen = 1024;
                StringBuilder sbInstallDir = new StringBuilder(installDirLen);

                MsiGetProductInfo(sbProductCode.ToString(),
                    "InstallLocation", sbInstallDir, ref installDirLen);

                Console.WriteLine("ProductName {0}: {1}", 
                    sbProductName, sbInstallDir);
            }
        }
    }
}
Dirk Vollmar
  • 172,527
  • 53
  • 255
  • 316
  • Like w/ WiX, this doesn't seem to enumerate all products. Is there a reason for that? `MsiEnumProducts` doc does not seem to mention anything about this. So I ended up sticking w/ the registry-way (iterating `Microsoft\Windows\CurrentVersion\Uninstall` keys). https://stackoverflow.com/questions/20708136/unable-to-query-registry-for-installed-msi-files#comment102393783_20710736 – sjlewis Sep 18 '19 at 13:13
8

You can ask the WMI Installed applications classes: the Win32_Products class represents all products installed by Windows Installer. For instance the following PS script will retrieve all prodcuts installed on local computer that were installed by Windows Installer:

Get-WmiObject -Class Win32_Product -ComputerName .

See Working with Software Installations. POrting the PS query to the equivalent C# use of WMI API (in other words Using WMI with the .NET Framework) is left as an exercise to the reader.

Remus Rusanu
  • 288,378
  • 40
  • 442
  • 569
  • Awesome Powershell command! I had fun writing a script that would compare what is installed on computers A and B but not C and D (Something worked on the first group, but not the second :) ). – Hamish Grubijan Oct 19 '10 at 18:42
  • 1
    Beware using Win32_Product because of some nasty side-effects as documented here [link](http://sdmsoftware.com/wmi/why-win32_product-is-bad-news/) – Bob M Mar 19 '13 at 17:17
0

Well if all the programs you need store their install paths on the registry you can use something like: http://visualbasic.about.com/od/quicktips/qt/regprogpath.htm (I know it's VB but same principle).

I'm sure there probably is a way to get the Program list via .NET if some don't store their install paths (or do it obscurely), but I don't know it.

Blam
  • 2,888
  • 3
  • 25
  • 39
-1

The simplest methods via registry

using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;


namespace SoftwareInventory
{
    class Program
    {
        static void Main(string[] args)
        {
            //!!!!! Must be launched with a domain administrator user!!!!!
            Console.ForegroundColor = ConsoleColor.Green;
            StringBuilder sbOutFile = new StringBuilder();
            Console.WriteLine("DisplayName;IdentifyingNumber");
            sbOutFile.AppendLine("Machine;DisplayName;Version");

            //Retrieve machine name from the file :File_In/collectionMachines.txt
            //string[] lines = new string[] { "NameMachine" };
            string[] lines = File.ReadAllLines(@"File_In/collectionMachines.txt");
            foreach (var machine in lines)
            {
                //Retrieve the list of installed programs for each extrapolated machine name
                var registry_key = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
                using (Microsoft.Win32.RegistryKey key = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, machine).OpenSubKey(registry_key))
                {
                    foreach (string subkey_name in key.GetSubKeyNames())
                    {
                        using (RegistryKey subkey = key.OpenSubKey(subkey_name))
                        {
                            //Console.WriteLine(subkey.GetValue("DisplayName"));
                            //Console.WriteLine(subkey.GetValue("IdentifyingNumber"));
                            if (subkey.GetValue("DisplayName") != null && subkey.GetValue("DisplayName").ToString().Contains("Visual Studio"))
                            {
                                Console.WriteLine(string.Format("{0};{1};{2}", machine, subkey.GetValue("DisplayName"), subkey.GetValue("Version")));
                                sbOutFile.AppendLine(string.Format("{0};{1};{2}", machine, subkey.GetValue("DisplayName"), subkey.GetValue("Version")));
                            }
                        }
                    }
                }
            }
            //CSV file creation
            var fileOutName = string.Format(@"File_Out\{0}_{1}.csv", "Software_Inventory", DateTime.Now.ToString("yyyy_MM_dd_HH_mmssfff"));
            using (var file = new System.IO.StreamWriter(fileOutName))
            {

                file.WriteLine(sbOutFile.ToString());
            }
            //Press enter to continue 
            Console.WriteLine("Press enter to continue !");
            Console.ReadLine();
        }


    }
}
Domenico Zinzi
  • 954
  • 10
  • 18