Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing the WindowsInstaller.Installer COM Object in C#

I found a nice Powershell function on GitHub which uses the WindowsInstaller.Installer COM object to query applications installed and lists all the details and properties quite beautifully. However, I don't want to use Powershell I want to create an exe.

I don't want to search the registry and I don't want to use WMI. I want to use the same exact method used in the powershell script and another vbscript I found. There does exist a COM object called WindowsInstaller.Installer. It definitely exists and yet for some reason I can't find a single example of how it would be accessed after importing the msi.dll in Visual Studio using C#.

Does anyone know the answer to this question?

In Visual Studio, the WindowsInstaller.Installer reference from adding the COM reference for WindowsInstaller is just a type and nothing more. It contains no method called "GetType" and trying to convert this PowerShell to C# is not working right.

I also don't know what @{} means but I guess it means Hashtable.

My sad attempt at forcing the situation below:

    private void Form1_Load(object sender, EventArgs e)
    {
        Type installerType = Type.GetType("WindowsInstaller.Installer");

        Installer installerObj = (Installer)Activator.CreateInstance(installerType);
        WindowsInstaller.Installer installer = installerObj as WindowsInstaller.Installer;
        var type = installer.GetType();

        var Products = type.InvokeMember("Products", System.Reflection.BindingFlags.GetProperty, null, installer, null) as IEnumerable<object>;

        foreach (var product in Products)
        {
            Hashtable hash = new Hashtable();
            hash.Add("ProductCode", product);

            string[] Attributes = { "Language", "ProductName", "PackageCode", "Transforms", "AssignmentType", "PackageName", "InstalledProductName", "VersionString", "RegCompany", "RegOwner", "ProductID", "ProductIcon", "InstallLocation", "InstallSource", "InstallDate", "Publisher", "LocalPackage", "HelpLink", "HelpTelephone", "URLInfoAbout", "URLUpdateInfo" };
            foreach (var attribute in Attributes)
            {
                object[] thing = { product, attribute };
                var details = type.InvokeMember("ProductInfo", System.Reflection.BindingFlags.GetProperty, null, installer, thing);
                hash.Add(attribute, details);
            }
            new ??????????
        }

    }
like image 413
Nathan McKaskle Avatar asked Oct 12 '25 05:10

Nathan McKaskle


1 Answers

You're right about @{}, its a new hashtable. I think for what you want you can just create a list and every product that gets iterated through to collect properties you can add it to that list

   Type installerType = Type.GetTypeFromProgID("WindowsInstaller.Installer");

        Installer installerObj = (Installer)Activator.CreateInstance(installerType);
        WindowsInstaller.Installer installer = installerObj as WindowsInstaller.Installer;
        var Products = installerObj.Products;

        List<Hashtable> ProductCollection = new List<Hashtable>();

        foreach (var product in Products)
        {
            Hashtable hash = new Hashtable();
            hash.Add("ProductCode", product);

            string[] Attributes = { "Language", "ProductName", "PackageCode", "Transforms", "AssignmentType", "PackageName", "InstalledProductName", "VersionString", "RegCompany", "RegOwner", "ProductID", "ProductIcon", "InstallLocation", "InstallSource", "InstallDate", "Publisher", "LocalPackage", "HelpLink", "HelpTelephone", "URLInfoAbout", "URLUpdateInfo" };
            foreach (var attribute in Attributes)
            {
                try
                {
                    var details = installer.ProductInfo[product.ToString(), attribute.ToString()];
                    hash.Add(attribute, details);
                }
                catch
                {
                }
            }
            ProductCollection.Add(hash);
        }

Now just reference ProductCollection to get your product details. If you want to go a step further you can create a class for each MSI and have your process create an object for each product.

public class MSIInfo
    {
        public string ProductCode { get; set; }
        public string Language { get; set; }
        public string ProductName { get; set; }
        public string PackageCode { get; set; }
        public string Transforms { get; set; }
        public string AssignmentType { get; set; }
        public string PackageName { get; set; }
        public string InstalledProductName { get; set; }
        public string VersionString { get; set; }
        public string RegCompany { get; set; }
        public string RegOwner { get; set; }
        public string ProductID { get; set; }
        public string ProductIcon { get; set; }
        public string InstallLocation { get; set; }
        public string InstallSource { get; set; }
        public string InstallDate { get; set; }
        public string Publisher { get; set; }
        public string LocalPackage { get; set; }
        public string HelpLink { get; set; }
        public string HelpTelephone { get; set; }
        public string URLInfoAbout { get; set; }
        public string URLUpdateInfo { get; set; }

        public override string ToString()
        {
            return $"{ProductName} - {ProductCode}";
        }

        public static IEnumerable<MSIInfo> GetProducts()
        {
            Type installerType = Type.GetTypeFromProgID("WindowsInstaller.Installer");

            Installer installerObj = (Installer)Activator.CreateInstance(installerType);
            WindowsInstaller.Installer installer = installerObj as WindowsInstaller.Installer;
            var Products = installerObj.Products;

            List<MSIInfo> ProductCollection = new List<MSIInfo>();

            foreach (var product in Products)
            {
                MSIInfo msi = new MSIInfo();
                msi.ProductCode = product.ToString();

                foreach (var property in msi.GetType()?.GetProperties())
                {
                    try
                    {
                        if (property.Name != "ProductCode")
                        {
                            string val = installer.ProductInfo[product.ToString(), property.Name];
                            property.SetValue(msi, val);
                        }
                    }
                    catch (System.Runtime.InteropServices.COMException)
                    {
                    }
                }
                ProductCollection.Add(msi);
            }

            return ProductCollection;

        }
    }

Once you have that class you can get the collection from your code like this

var Products = MSIInfo.GetProducts();
like image 145
Paul G Avatar answered Oct 14 '25 19:10

Paul G