为什么 vbs 能够在同时使用 DTF 和 MSI API 的 C# 中找到 INSTALLLOCATION?

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

VBS 按我的要求工作,但 COM API 和使用 C# 的 DTF 都没有找到 InstallLocation。以下是我到目前为止所做的。


感谢 this post,我能够使用 vbs 找到在注册表中不可用的 InstallLocation。我知道 vbs 正在调用 %WINDIR%\system32\msi.dll.

上可用的 COM API

C# COM API

所以我想我会用C#来调用这个方法。但它失败了。即使我可以确认存在和安装,它也无法打开其中一个产品 GUID(我检查了三遍)。

注意:有些产品没有抛出异常,并且正确找到了 InstallLocation。这还不是全部。

以下是我的代码。

        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;
        }

好的,没用,让我们试试 DTF。


C# DTF

但是那也没有成功。以下是我的代码。这不会触发异常,甚至无法通过 COM API 检测到的异常也能够检测到自身,但 InstallLocation 属性 是空字符串。

注意:有些产品确实填写了 InstallLocation 属性。这还不是全部。

        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;
        }

为什么 VBS 能够检测到 InstallLocation 而 C# 都不能?我错过了什么?

我不能使用VBS的原因是try catch不可用,除非我使用vb.net。

根据 SteinAsmul 的建议 DTF 不会自动调用与成本相关的操作,我进一步阅读了 DTF 文档。

我发现 DoAction 在 DTF 中也可用。所以我使用了以下内容,var installLocation 现在有了我正在寻找的预期值。

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

您可以试试这个,尽管它不是很“完善”或经过测试。

使用 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"]);
        }
    }
}

使用 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();
        }
    }
}