Install files to existing program's install path with Visual Studio Setup project
我正在制作一个 Visual Studio 安装项目,它会为目标机器上的现有程序安装一个插件。我的文件需要进入该应用程序的安装目录。我希望能够干净地安装和卸载我的插件,而不会对应用程序本身产生不必要的影响。
现有程序的安装路径可以在注册表项中找到,并且可能因安装而异。
我能否将 Visual Studio 安装项目配置为从该注册表项读取值,然后将插件文件放入注册表指定的目录(或其子目录)中?我需要使用自定义操作还是可以使用标准的安装项目功能来实现?
我注意到在启动条件窗口中,我可以设置一个注册表搜索启动条件,它根据特定的注册表项设置安装程序属性。我可以使用它来检索密钥的实际值以在"文件"窗口中使用,还是仅为启动条件设置真/假值?
啊,毕竟在 MSDN 文档中找到了答案。没有自定义操作也是可能的!
总结:
编辑:叹息。由于 Visual Studio 安装项目中的一个错误,这在 x64 上不起作用,至少自 VS2008 以来一直很突出,并且在 VS2015 中仍然存在。即使安装项目平台设置为 x64,注册表搜索操作始终搜索 x86 注册表配置单元,并且看不到 x64 HKLM\\\\\\\\SOFTWARE 键值。
详细信息:生成的 MSI 文件中的 RegLocator 表包含用于注册表搜索的数据。 Type 字段包含导致搜索为 64 位本机注册表的 msidbLocatorType64bit 值。添加此值以更正问题。手动(使用 Orca)是测试功能的快速方法。 RegLocator 表
错误引用 1
错误引用 2
我获得工作安装程序的最终解决方案是使用 WiX 创建一个基本安装程序,并完全放弃 Visual Studio 安装项目。
但是,在完全切换到 WiX 之前,我创建了一个小型 C# 控制台应用程序,该应用程序可以作为构建后事件调用,以编辑由 Visual Studio 安装项目生成的 MSI 文件。控制台应用程序基于 WiX 工具集随附的部署工具基础 (DTF)。 DTF 提供了一个用于编辑 MSI 文件的 C# API。这是它的主要内容,可能对未来的用户有用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | 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(); } } } } |