如何获取安装目录?

8

MSI存储安装目录以供未来的卸载任务使用。

只有安装程序设置了ARPINSTALLLOCATION属性,才能使用INSTALLPROPERTY_INSTALLLOCATION属性(即"InstallLocation")。但是,该属性是可选的,几乎没有人使用它。

我如何检索安装目录?

6个回答

2
使用注册表键来跟踪安装目录,这样在升级和删除产品时就可以引用它。使用WIX,我会创建一个组件来创建该键,在安装目录声明的Directy标签之后。

你使用什么工具来创建MSI文件?每种语言都有其规范来获取这些信息。 - CheGueVerra
我用的是InstallShield 11.5制作的(我知道它已经过时了...)。BasicMSI项目。 - Michael Damatov

1
我会使用MsiGetComponentPath()函数 - 你需要提供ProductId和ComponentId,但是你可以得到已安装文件的完整路径 - 只需选择一个指向你安装目录位置的路径即可。如果你想获取任意随机MSI文件的目录值,我不认为有API可以让你这样做。

0

如在帖子的其他地方所述,我通常会在HKLM中编写注册表键,以便能够轻松检索安装目录以供后续安装使用。

在处理未执行此操作的设置时,我使用内置的Windows Installer功能AppSearch:http://msdn.microsoft.com/en-us/library/aa367578(v=vs.85).aspx 通过指定要查找的文件签名来定位先前安装的目录。

文件签名可以由文件名、文件大小和文件版本以及其他文件属性组成。每个签名都可以以一定的灵活性指定,因此您可以通过指定要查找的版本范围来查找同一文件的不同版本。请查看SDK文档:http://msdn.microsoft.com/en-us/library/aa371853(v=vs.85).aspx

在大多数情况下,我使用主应用程序EXE并设置紧密签名,通过查找具有正确版本和日期的文件的狭窄版本范围来实现。


0
我会尝试使用Installer.OpenProduct(productcode)。这将打开一个会话,在该会话中,您可以请求Property("TARGETDIR")。

0
试试这个: var sPath = this.Context.Parameters["assemblypath"].ToString();

0

最近我需要通过 Ketarin 自动化安装 Natural Docs。我可以假设它已经安装到默认路径下 (%ProgramFiles(x86)%\Natural Docs),但我决定采取一种安全的方法。不幸的是,即使安装程序在 HKLM\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall 上创建了一个键,但其中任何值都没有导致我找到安装目录。

Stein 的回答提出了 AppSearch MSI 函数,看起来很有趣,但不幸的是 Natural Docs MSI 安装程序没有提供 Signature 表,使其方法无法奏效。

所以我决定搜索注册表以找到任何与 Natural Docs 安装目录相关的引用,结果在 HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components 键中找到了一个。

MSI Components Registry Key

我在C#中为Ketarin开发了一个Reg类,允许递归。因此,我通过HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components查找所有值,如果主应用程序可执行文件(NaturalDocs.exe)在其中一个子键值中找到,则提取它(C:\Program Files (x86)\Natural Docs\NaturalDocs.exe变成C:\Program Files (x86)\Natural Docs),并将其添加到系统环境变量%PATH%中(这样我就可以直接调用“NaturalDocs.exe”而不是使用完整路径)。
Registry "class"(实际上是函数)可以在GitHub上找到(RegClassCS)。
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo("NaturalDocs.exe", "-h");
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;

var process = System.Diagnostics.Process.Start (startInfo);
process.WaitForExit();

if (process.ExitCode != 0)
{
    string Components = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components";

    bool breakFlag = false;

    string hKeyName = "HKEY_LOCAL_MACHINE";
    if (Environment.Is64BitOperatingSystem)
    {
        hKeyName = "HKEY_LOCAL_MACHINE64";
    }

    string[] subKeyNames = RegGetSubKeyNames(hKeyName, Components);
    // Array.Reverse(subKeyNames);
    for(int i = 0; i <= subKeyNames.Length - 1; i++)
    {
        string[] valueNames = RegGetValueNames(hKeyName, subKeyNames[i]);
        foreach(string valueName in valueNames)
        {
            string valueKind = RegGetValueKind(hKeyName, subKeyNames[i], valueName);
            switch(valueKind)
            {
                case "REG_SZ":
                // case "REG_EXPAND_SZ":
                // case "REG_BINARY":
                    string valueSZ = (RegGetValue(hKeyName, subKeyNames[i], valueName) as String);
                    if (valueSZ.IndexOf("NaturalDocs.exe") != -1)
                    {
                        startInfo = new System.Diagnostics.ProcessStartInfo("setx", "path \"%path%;" + System.IO.Path.GetDirectoryName(valueSZ) + "\" /M");
                        startInfo.Verb = "runas";

                        process = System.Diagnostics.Process.Start (startInfo);
                        process.WaitForExit();

                        if (process.ExitCode != 0)
                        {
                            Abort("SETX failed.");
                        }

                        breakFlag = true;
                    }
                    break;

                /*  
                case "REG_MULTI_SZ":
                    string[] valueMultiSZ = (string[])RegGetValue("HKEY_CURRENT_USER", subKeyNames[i], valueKind);

                    for(int k = 0; k <= valueMultiSZ.Length - 1; k++)
                    {
                        Ketarin.Forms.LogDialog.Log("valueMultiSZ[" + k + "] = " + valueMultiSZ[k]);
                    }
                    break;
                */

                default:
                    break;
            }

            if (breakFlag)
            {
                break;
            }
        }

        if (breakFlag)
        {
            break;
        }
    }
}

即使您不使用Ketarin,也可以轻松地将函数粘贴并通过Visual Studio或CSC构建。

更一般的方法是使用RegClassVBS,它允许注册表键递归,并且不依赖于.NET Framework平台或构建过程。

请注意,枚举组件键的过程可能会占用CPU。上面的示例具有Length参数,您可以使用它来向用户显示一些进度(例如“i from (subKeysName.Length-1) keys remaining”-创意)。在RegClassVBS中也可以采用类似的方法。

RegClassCS和RegClassVBS两个类都有文档和示例可供参考,您可以在任何软件中使用它们并为其开发做出贡献,通过在git repo上进行提交,并在其github页面上打开问题(当然,如果您遇到无法解决的问题,我们可以尝试重现该问题以找出解决方案。=)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接