【问题标题】:How to get the installation directory?如何获取安装目录?
【发布时间】:2010-09-20 08:00:41
【问题描述】:

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

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

如何找回安装目录?

【问题讨论】:

    标签: windows deployment installation windows-installer


    【解决方案1】:

    使用注册表项来跟踪您的安装目录,这样您就可以在升级和删除产品时引用它。

    使用 WIX,我将在安装目录的 Directy 标记声明之后创建一个创建密钥的组件

    【讨论】:

    • 您使用什么来创建 MSI 文件,每种语言都有获取该信息的规范
    • 我已经使用 InstallShield 11.5 实现了它(我知道它已经过时了......)。 BasicMSI 项目。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          【解决方案5】:

          正如线程中其他地方所述,我通常在 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 并通过寻找具有正确版本和日期的文件的窄版本范围来设置严格的签名。

          【讨论】:

            【解决方案6】:

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

            Stein 的回答建议使用 AppSearch MSI 功能,它看起来很有趣,但遗憾的是,Natural Docs MSI 安装程序没有为他的方法工作提供签名表。

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

            我在 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”而不是使用完整路径)。

            注册表“类”(实际上是函数)可以在 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 页面,以便我们尝试重现该问题以找出我们可以做些什么。 =)

            【讨论】:

              猜你喜欢
              • 2016-07-11
              • 1970-01-01
              • 2022-01-23
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-03-03
              • 1970-01-01
              相关资源
              最近更新 更多