Why is vbs able to find the INSTALLLOCATION when C# using both DTF and MSI API cannot?

227 views Asked by At

VBS works as I desired, but both COM API and DTF using C# is not locating the InstallLocation. Followings are what I have done so far.


Thanks to this post, I was able to find a InstallLocation that is not available on registry using vbs. I understand that vbs is calling for COM API available on %WINDIR%\system32\msi.dll.


C# COM API

So I thought I would use C# to call this method up. But it failed. Even though I can confirm the existence and installation, it cannot open one of the product GUID (I tripple checked).

Note: there were products that did not throw exception and InstallLocation were properly found. Its just not all.

Followings are my code.

        static Dictionary<string, string> FindInstallLocationsCOM(Dictionary<string, string> products)
        {
            var locationDictionary = new Dictionary<string, string>();

            // Get the type of the Windows Installer object
            Type installerType = Type.GetTypeFromProgID("WindowsInstaller.Installer");

            // Create the Windows Installer object
            Object installerObj = Activator.CreateInstance(installerType);
            Installer installer = installerObj as Installer;

            foreach (var product in products)
            {
                try
                {
                    var session = installer.OpenProduct(product.Value);
                    if (session != null)
                    {
                        session.DoAction("CostInitialize");
                        session.DoAction("CostFinalize");
                        var installLocation = session.Property["INSTALLLOCATION"];
                        MessageBox.Show(product.Key + "\n" + "Product Code : " + product.Value + "\n" + "Install Location : " + installLocation);
                        locationDictionary.Add(product.Key, installLocation);
                    }
                }
                catch (Exception e)
                {
                    MessageBox.Show("Error : Could not open Product " + e.Message + "\n" + "Product : " + product.Key + "\n" + "Product Code : " + product.Value);
                }
            }

            return locationDictionary;
        }

OK that did not work, let's try DTF.


C# DTF

But that also was not successful. Following is my code. This does not trigger exception, and even the one that was not detectable via COM API was able to detect itself, but InstallLocation property was empty string.

Note: there were products that did have InstallLocation property filled. Its just not all.

        static Dictionary<string,string> FindInstallLocation(Dictionary<string,string> products)
        {
            var locationDictionary = new Dictionary<string, string>();

            foreach (var product in products)
            {
                try
                {
                    var installed = new ProductInstallation(product.Value);
                    if (installed != null)
                    {
                        var installLocation = installed.InstallLocation;
                        MessageBox.Show(product.Key + "\n" + "Product Code : " + product.Value + "\n" + "Install Location : " + installLocation);
                        locationDictionary.Add(product.Key, installLocation);
                    }
                    else
                    {
                        MessageBox.Show(product.Key + "\n" + "Product Code : " + product.Value + "\n" + "Is not installed");
                    }
                }
                catch (Exception e)
                {
                    MessageBox.Show("Error :  " + e.Message + "\n" + "Product : " + product.Key + "\n" + "Product Code : " + product.Value);
                }
            }

            return locationDictionary;
        }

Why is VBS able to detect the InstallLocation when neither of C# is not able to? What am I missing?

The reason I cannot use VBS is because the try catch is not available unless I use vb.net.

2

There are 2 answers

4
Shintaro Takechi On BEST ANSWER

After SteinAsmul's suggesion that DTF does not automatically call cost related action, I did further reading in the DTF document.

I found an DoAction is also available in DTF. So I used the following, and the var installLocation now has the expected value I was looking for.

Installer.SetInternalUI(InstallUIOptions.Silent);
var session = Installer.OpenProduct(product.Value);
session.DoAction("CostInitialize");
session.DoAction("CostFinalize");
var installLocation = session["INSTALLLOCATION"];
session.Close();
6
Stein Åsmul On

You can try this though it is not very "polished" or tested.

Using COM:

using System;
using System.Windows.Forms;
using WindowsInstaller;

namespace DTFTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Type installerType = Type.GetTypeFromProgID("WindowsInstaller.Installer");
            var installer = (WindowsInstaller.Installer)Activator.CreateInstance(installerType);
            if (installer == null) { return; }

            var session = installer.OpenProduct("Product-GUID-here");
            if (session == null) { return; }

            session.DoAction("CostInitialize");
            session.DoAction("CostFinalize");

            MessageBox.Show(session.Property["Directory-Property-Here"]);
        }
    }
}

Using DTF:

using System;
using Microsoft.Deployment.WindowsInstaller;
using System.Windows.Forms;

namespace DTFTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Installer.SetInternalUI(InstallUIOptions.Silent);
            var session = Installer.OpenProduct("Product-GUID-here");
            session.DoAction("CostInitialize");
            session.DoAction("CostFinalize");
            MessageBox.Show(session["Directory-Property-Here"]);
            session.Close();
        }
    }
}