0

I need some help in retrieving the ProductVersion of a program in C#. I did find a way to retrieve it using WMI but it is really slow.

Previously I had the same issue when using vbscript and the solution was to use the Windows Installer (which is really fast)

Set objInstaller = CreateObject("WindowsInstaller.Installer")
Set colProducts = objInstaller.Products

For Each objProduct In colProducts
    strProductName = objInstaller.ProductInfo(objProduct, "ProductName")
    If strProductName = "MyProductName1" Then       
        var1 = objInstaller.ProductInfo(objProduct, "VersionString")
    End If
    If strProductName = "MyProductName2" Then       
        var2 = objInstaller.ProductInfo(objProduct, "VersionString")
    End If              
Next

The question is how do I do the same in C#? I'm currently using Avalonia as UI.

I also tried all the other methods in the search (Wix DTF, COM with P/Invoke), so please don't redirect to google....

Edit: I do not have a path for the msi or exe. I need to search in the registry or installer, that is why getfileversion or dtf is not working.

Thanks!

Edit1: Here is my code after reading all the comments and resources.

public class Program
{
    public static string getInstalledVersion(string findByName)
    {
        string info = null;
        string registryKey = @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall";

        RegistryKey key32 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32);
        RegistryKey key = key32.OpenSubKey(registryKey);

        if (key != null)
        {
            foreach (RegistryKey subkey in key.GetSubKeyNames().Select(keyName => key.OpenSubKey(keyName)))
            {
                string displayName = subkey.GetValue("DisplayName") as string;

                if (displayName != null && displayName.Contains(findByName))
                    info = subkey.GetValue("Version").ToString();
                    break;
                else
                    info = "Not found";
            }
            key.Close();
        }
        return info;
    }
}

Get the result in a variable:

public string MyVar => Program.getInstalledVersion("Microsoft Edge");

Edit2: So the edit with the break in my latest version somewhat works. It finds Microsoft Edge but still doesn't find other programs (i will try other paths as fallbacks)

@ Stein Asmul i did try your version but doesn't find anything, maybe Im doing something wrong?

public class ProgramX
{
    public static string getPVersion(string findByName)
    {
        string info = null;
        foreach (var p in ProductInstallation.AllProducts)
        {
            if (p.ProductName == findByName)
            {
                info = p.ProductVersion.ToString();
                break;
            }
            else
                info = "Not Found";
        }
        return info;
    }
}

Call:

public string MyProgram => ProgramX.getPVersion("Microsoft Edge");

Edit3: Great success! I managed to get it to work. The problem was subkey getvalue is "DisplayVersion" not "Version". I only need 64 for my programs.

    public class ProgramI
    {
        public static string getInstalledVersion(string findByName)
        {
            string info = null;
            string registryKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";

            RegistryKey key64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
            RegistryKey key = key64.OpenSubKey(registryKey);

            if (key != null)
            {
                foreach (RegistryKey subkey in key.GetSubKeyNames().Select(keyName => key.OpenSubKey(keyName)))
                {
                    string displayName = subkey.GetValue("DisplayName") as string;

                    if (displayName != null && displayName.Contains(findByName))
                    {
                        info = subkey.GetValue("DisplayVersion").ToString();
                        break;
                    }
                    else
                        info = "Not found";
                }
                key.Close();
            }

            return info;
        }

    }

Thanks everyone!

Lodes
  • 21
  • 6
  • This document can help? https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.fileversioninfo.productversion – emoacht Aug 24 '22 at 21:15
  • I need to search Installed Programs, I don't have a exe, msi path. – Lodes Aug 25 '22 at 04:01
  • Duplicate to https://stackoverflow.com/questions/54224817/accessing-the-windowsinstaller-installer-com-object-in-c-sharp – Lex Li Aug 25 '22 at 04:22
  • @Lodes If so, searching the registry could be easier. https://stackoverflow.com/a/908907/3137337 Use `DisplayVersion` instead of `DisplayName`. – emoacht Aug 25 '22 at 04:29
  • @Lodes If you're trying to find version using product name then how would you know whether the application is 32-bit or 64-bit? – Vivek Jaiswal Aug 26 '22 at 11:41

3 Answers3

0

There are many ways to do this. Using WiX DTF should be relatively easy, but you might not want the dependency on the DTF dlls? If so you can use interop and call the C++ API directly (no custom assembly dependencies).

For the DTF scenario (too late to add the C++ api call version tonight) you create a new project, then you add project references to the WiX DTF assemblies (standard path is: "C:\Program Files (x86)\WiX Toolset v3.11\bin"). The files you need are:

  • Microsoft.Deployment.WindowsInstaller.dll
  • Microsoft.Deployment.WindowsInstaller.Package.dll

Then you can try this code snippet - consult the DTF help file for more details - the help file you will find in the WiX installation folder ("doc" sub-folder):

using System;
using Microsoft.Deployment.WindowsInstaller;
using Microsoft.Deployment.WindowsInstaller.Package;

namespace DTFTestApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            InstallPackage pkg = new InstallPackage(@"e:\MySetup.msi", DatabaseOpenMode.ReadOnly);
            var version = pkg.Property["ProductVersion"];
            Console.WriteLine(version);
        }
    }
}

Links:

Stein Åsmul
  • 39,960
  • 25
  • 91
  • 164
  • I did try this but I don't have a path for an msi or exe. Only have the name of the installed program. Can I go directly to product name with DTF? – Lodes Aug 25 '22 at 04:03
0

Product GUID Lookup: If you know the product GUID for the product (How can I find the product GUID of an installed MSI setup?), you can simply get the version and other properties for this product GUID:

using System;
using Microsoft.Deployment.WindowsInstaller;
using Microsoft.Deployment.WindowsInstaller.Package;

namespace DTFTestApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            ProductInstallation i = new ProductInstallation("{8D6DECA5-17F9-42AF-A62B-8D7C5C11069B}");
            Console.WriteLine(i.ProductVersion);            
        }
    }
}

Product Name Lookup: If you need to search for your product by name I guess this could do:

using System;
using Microsoft.Deployment.WindowsInstaller;

namespace DTFTestApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            foreach (var p in ProductInstallation.AllProducts)
            {
                if (p.ProductName == "Inkscape")
                {
                    Console.Write("Inkspace found in version: " + p.ProductVersion + "\n");
                }
            }        
        }
    }
}

You can see this code snippet for more terse, advanced constructs using Linq. For example something like this:

var myproduct = ProductInstallation.AllProducts.FirstOrDefault(p => p.ProductName == "MyProductName");

MSI Package Estate: The link below takes you to a VBScript which will create a HTML-report of the MSI package estate: https://github.com/glytzhkof/all/blob/master/MsiHtmlReport-Mini-V4.vbs

Sample screenshot:

MSI Package Estate


Links:

Stein Åsmul
  • 39,960
  • 25
  • 91
  • 164
  • [Try to run that VBScript](https://github.com/glytzhkof/all/blob/master/MsiHtmlReport-Mini-V4.vbs). The full list of MSI packages might give you a clue? The name you search for is wrong? Save the VBScript to the desktop and run it from there. – Stein Åsmul Aug 25 '22 at 14:08
0

You need to put a break in if statement.

                if (displayName != null && displayName.Contains(findByName)){
                info = subkey.GetValue("Version").ToString();
                break;
                }
                else
                info = "Not found";

In your case, Display Name is found but the next iteration of for loop setting it to "not found". Also, you need to add fallback mechanism in your code so that if it doesn't found name in 64-bit node then it should search in 32-bit one.

Vivek Jaiswal
  • 861
  • 1
  • 7
  • 20
  • yes thank you.. I finally was able to see a version for Microsoft Edge, but for other custom programs I get some weird numbers which are not the version. – Lodes Aug 25 '22 at 14:15