installutil bindingRedirect
I have a windows service that depends on a 3:rd party API
The API is already installed in the GAC on the client computer
There are several versions of the API (1.0.0.0, 1.1.0.0 etc)
My service works with all versions of the API
I use a bindingRedirect tag in the app.config file which works fine when running the service.
Problem is that the app.config file is not used when running InstallUtil so I get a binding exception when registering the service开发者_运维百科.
Currently I use "sc create" to manually register the service but is there a better way?
(without editing machine.config etc)I just ran in to this, the only solution I could find is from https://connect.microsoft.com/VisualStudio/feedback/details/525564/installutil-exe-does-not-honor-app-config-especially-binding-information:
As a workaround, you might be able to make this work by modifying the InstallUtil.exe.config file to contain the binding information. The InstallUtil.exe.config is installed to %WinDir%\Microsoft.NET\Framework\\InstallUtil.exe.config where is the version of framework you're using.
I came up with another workaround to install service with binding redirects. As I have a lot of services, this is what I decided to go after.
Change Windows installer to Console app and implement functionality to self install (using command line and ManagedInstallerClass.InstallHelper).
Implement an installer class capable of executing command line in a completely separate assembly, for example
CommandLineInstaller.DLL. CommandLineInstaller.DLL
shall implement methods Install/Uninstall/Rollback identically - execute a command line with parameters such as:FileName, WorkingDirectory, Args, WindowStyle
.Modify setup project to deploy both 1) service and b)
CommandLineInstaller.DLL
Modify setup project custom actions: instead of running actions of service, run actions of CommandLineInstaller.DLL. CustomActionData property for Install action will look like:
/FileName="[TARGETDIR]MyService.exe" /Args="/install" WindowStyle="Hidden"
Action configuration: Install: myservice /install Rollback: myservice /uninstall Uninstall: myservice /uninstall
No need to write Commit, AFAIK.
Now, setup project will execute CommandLineInstaller.DLL installer in its own process. And then CommandLineInstaller.DLL will in turn launch MyService.exe in its own process with bloody binding redirects as they should be.
PS MyService.exe
can use exit code mechanism to inform installer about failures and I highly recommend checking them from CommandLineInstaller.
Hopefully it's a good enough outline.
PS Be mindful of TARGETDIR needs to have a slash when passed by itself to directories:
/WorkDir="[TARGETDIR]\"
Example of Install CustomActionData:
/FileName="[TARGETDIR]\MyService.exe" /Args="/install" /WorkingDir="[TARGETDIR]\" /ValidExitCode="0" /WindowStyle="Normal"
Some code:
using System;
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
namespace QT.Install
{
[RunInstaller(true)]
public partial class ExecuteCommandInstaller : System.Configuration.Install.Installer
{
public class CommandArgs
{
public string FileName { get; set; }
public string WorkingDir { get; set; }
public string Args { get; set; }
public string ValidExitCode { get; set; }
public ProcessWindowStyle WindowStyle { get; set; }
}
public ExecuteCommandInstaller()
{
InitializeComponent();
}
public override void Install(IDictionary stateSaver)
{
base.Install(stateSaver);
ExecuteCommand(stateSaver);
}
public override void Commit(IDictionary savedState)
{
base.Commit(savedState);
ExecuteCommand(savedState);
}
public override void Uninstall(IDictionary savedState)
{
base.Uninstall(savedState);
ExecuteCommand(savedState);
}
public override void Rollback(IDictionary savedState)
{
base.Rollback(savedState);
ExecuteCommand(savedState);
}
private void ExecuteCommand(IDictionary stateSaver)
{
CommandArgs commandArgs = new CommandArgs()
{
FileName = StripDoubleSlash(Context.Parameters["FileName"] ?? ""),
WorkingDir = StripDoubleSlash(Context.Parameters["WorkingDir"] ?? ""),
Args = Context.Parameters["Args"] ?? "",
ValidExitCode = Context.Parameters["ValidExitCode"] ?? "*"
};
try
{
commandArgs.WindowStyle = (ProcessWindowStyle)Enum.Parse(typeof(ProcessWindowStyle), Context.Parameters["WindowStyle"] ?? "Hidden");
}
catch (Exception err)
{
throw new Exception($"Invalid WindowStyle parameter value: {Context.Parameters["WindowStyle"]}", err);
}
InternalExecuteCommand(commandArgs);
}
private void InternalExecuteCommand(CommandArgs commandArgs)
{
if (string.IsNullOrEmpty(commandArgs.FileName))
throw new Exception("FileName is not specified.");
System.Diagnostics.ProcessStartInfo startInfo = new ProcessStartInfo(commandArgs.FileName, commandArgs.Args);
if (!string.IsNullOrEmpty(commandArgs.WorkingDir))
startInfo.WorkingDirectory = commandArgs.WorkingDir;
startInfo.WindowStyle = commandArgs.WindowStyle;
using (var process = Process.Start(startInfo))
{
process.WaitForExit();
if (commandArgs.ValidExitCode != "*")
{
if (process.ExitCode.ToString() != commandArgs.ValidExitCode)
throw new Exception($"Executing {commandArgs.FileName} {commandArgs.Args} returned exit code {process.ExitCode}. Expected exit code is: {commandArgs.ValidExitCode}.");
}
}
}
private static string StripDoubleSlash(string value)
{
return value.Replace("\\\\", "\\");
}
}
}
精彩评论