使用Visual Studio安装项目将文件安装到现有程序的安装路径

时间:2018-01-16 03:20:56

标签: visual-studio-2015 visual-studio-setup-proje

我正在制作一个Visual Studio安装项目,该项目为目标计算机上的现有程序安装插件。我的文件需要进入该应用程序的安装目录。我希望能够干净地安装和卸载我的插件,而不会过多地影响应用程序本身。

现有程序的安装路径可以在注册表项中找到,并且可能因各个安装而异。

我可以配置Visual Studio安装项目以从该注册表项中读取值,然后将插件文件放入注册表指定的目录(或其子目录)中吗?我是否需要使用自定义操作,还是可以使用标准的安装项目功能来实现?

我注意到在启动条件窗口中,我可以设置注册表搜索启动条件,该条件根据特定的注册表项设置安装程序属性。我可以使用它来检索要在“文件”窗口中使用的密钥的实际值,还是仅为启动条件设置真值/假值?

1 个答案:

答案 0 :(得分:1)

啊,毕竟在MSDN documentation找到了答案。没有自定义操作就可以!

要点:

  1. 在启动条件窗口中的"搜索目标计算机"节点,添加注册表搜索操作。配置" RegKey"和"价值"用于指定注册表项值的名称的属性,该值包含插件需要安装到的安装路径。设置"属性"注册表搜索操作的属性为合理的名称,例如" ProductInstallPath"
  2. (可选)在“启动条件”节点下,添加“启动条件”并将其“条件”属性设置为[ProductInstallPath]。我认为这将检查注册表项值是否存在,并且在安装程序运行时为非null。
  3. 在"文件系统"安装项目的窗口,右键单击"目标机器上的文件系统"并选择"添加特殊文件夹","自定义文件夹"
  4. 将新文件夹的默认位置属性设置为[ProductInstallPath]
  5. 编辑:叹息。这在x64上不起作用,因为Visual Studio安装项目中存在一个错误,自VS2008以来至少出现并且仍然存在于VS2015中。即使安装项目平台设置为x64,注册表搜索操作也始终搜索x86注册表配置单元,并且无法查看x64 HKLM \ SOFTWARE键值。

    详细信息:生成的MSI文件中的RegLocator表包含注册表搜索的数据。 Type字段包含msidbLocatorType64bit值,该值使搜索成为64位本机注册表。添加此值以更正问题。手动(使用Orca)是一种快速测试功能的方法。 RegLocator table

    Bug Citation 1

    Bug Citation 2

    我获得有效安装程序的最终解决方案是使用WiX创建基本安装程序,并完全放弃Visual Studio安装项目。

    然而,在完全切换到WiX之前,我创建了一个小型C#控制台应用程序,可以将其作为构建后事件调用,以编辑Visual Studio安装项目生成的MSI文件。控制台应用程序基于WiX Toolset附带的部署工具基础(DTF)。 DTF提供了一个用于编辑MSI文件的C#API。这是它的核心,可能对未来的用户有用。

    using System;
    using System.IO;
    using Microsoft.Deployment.WindowsInstaller;
    
    /// <summary>
    /// This program patches the registry search key action in the MSI file produced by the Visual Studio Setup project,
    /// to correct x64 compatibility bugs in Visual Studio Setup Projects.
    /// </summary>
    /// <remarks>
    /// The two bugs are:
    /// 1) The Visual Studio setup project incorporates the 32-bit version of InstallUtilLib.dll, which can't load x64 assemblies for reflection
    /// See https://blogs.msdn.microsoft.com/heaths/2006/02/01/64-bit-managed-custom-actions-with-visual-studio/
    /// 2) Registry search actions don't set the x64 bit and therefore only search the 32-bit registry
    /// See https://social.msdn.microsoft.com/Forums/windows/en-US/40a2c1ee-7dd4-4289-a7d2-30b97239ae25/vs2005-setup-project-launch-conditions-registry-problem-on-x64-operating-systems
    /// </remarks>
    class SetupPatcher
    {
        static void Main(string[] args)
        {
            if (args.Length != 1)
            {
                Console.WriteLine("ERROR: Specify the name of the MSI file as the first parameter when calling this exe");
                Environment.Exit(1);
            }
    
            String msiName = args[0];
    
            using (var db = new Database(msiName, DatabaseOpenMode.Direct))
            {
                PatchInstallUtilLib(db);
                PatchRegLocator(db);
            }
        }
    
        /// <summary>
        /// Replace 32 bit InstallUtilLib.dll with x64 version
        /// </summary>
        /// <param name="db"></param>
        private static void PatchInstallUtilLib(Database db)
        {
            using (View view = db.OpenView(@"UPDATE `Binary` SET `Data` = ? WHERE `Name` = 'InstallUtil'"))
            {
                using (Record rec = new Record(1))
                {
                    String path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows),
                        @"Microsoft.NET\Framework64\v4.0.30319\InstallUtilLib.dll");
                    rec.SetStream(1, path);
                    view.Execute(rec);
                    db.Commit();
                }
            }
        }
    
        private static void PatchRegLocator(Database db)
        {
            // MSI SQL syntax documented here: https://msdn.microsoft.com/en-us/library/windows/desktop/aa372021.aspx
            // Schema of RegLocator table given at https://msdn.microsoft.com/EN-US/library/aa371171.aspx
            // Look for reg search actions of the Raw type in the HKLM registry root
            String registryKey = @"SOFTWARE\VendorName\ProductName";
    
            using (View view =
                db.OpenView(
                    @"UPDATE `RegLocator` SET `Type` = ? WHERE `Type` = {0} AND `Root` = {1} AND `Key` = '{2}'",
                    (Int32) LocatorTypes.RawValue, (Int32) RegistryRoot.LocalMachine, registryKey))
            {
                using (Record rec = new Record(1))
                {
                    rec.SetInteger(1, (Int32) (LocatorTypes.SixtyFourBit | LocatorTypes.RawValue));
                    view.Execute(rec);
                    db.Commit();
                }
            }
        }
    }