问题
I'm making a Visual Studio setup project which installs a plugin for an existing program on the target machine. My files need to go into the install directory for that application. I'd like to be able to cleanly install and uninstall my plugin without affecting the application itself more than necessary.
The existing program's install path can be found in a registry key, and may vary between individual installations.
Can I configure the Visual Studio setup project to read the value from that registry key and then put the plugin files into the registry-specified directory (or sub-directories thereof)? Would I need to use custom actions or can this be achieved using standard Setup Project features?
I notice that in the Launch Conditions window, I can set up a Registry Search Launch Condition which sets a Installer property based on a particular registry key. Can I use that to retrieve the actual value of the key for use in the Files window, or only to set a true/false value for purposes of Launch Conditions?
回答1:
Ah, found the answer in MSDN documentation after all. It is possible without custom actions!
Summary:
- In the Launch Conditions window, under "Search Target Machine" node, add a Registry Search action. Configure the "RegKey" and "Value" properties to specify the names of the registry key value which contains the install path that the plugin needs to be installed into. Set the "Property" property of the registry search action to a sensible name e.g. "ProductInstallPath"
- (Optional) under the Launch Conditions node, add a Launch Condition and set its Condition property to [ProductInstallPath]. I think this will check that the registry key value exists and is non-null when the installer runs.
- In the "File System" window for the Setup Project, right-click on "File System on Target Machine" and select "Add Special Folder", "Custom Folder"
- Set the Default Location property of the new folder to [ProductInstallPath]
EDIT: Sigh. This doesn’t work on x64 because of a bug in Visual Studio Setup Projects, outstanding since VS2008 at least and still present in VS2015. Even though the setup project platform is set to x64, the Registry Search Action always searches the x86 registry hive and can’t see x64 HKLM\SOFTWARE key values.
Details: The RegLocator table in the generated MSI file contains the data for the registry search. The Type field contains the msidbLocatorType64bit value that causes the search to be the 64-bit native registry. Add this value to correct the issue. Manually (with Orca) is a quick way to test the functionality. RegLocator table
Bug Citation 1
Bug Citation 2
My final solution for getting a working installer was to create a basic installer with WiX, and abandon Visual Studio Setup Projects entirely.
However, before switching entirely to WiX, I created a small C# console app which could be invoked as a post-build event to edit the MSI file produced by the Visual Studio Setup Project. The console app was based on the Deployment Tools Foundation (DTF) which is included with the WiX Toolset. DTF provides a C# API for editing MSI files. Here's the meat of it, which might be useful for future users.
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();
}
}
}
}
来源:https://stackoverflow.com/questions/48273612/install-files-to-existing-programs-install-path-with-visual-studio-setup-projec