Installutil bindingRedirect

I have a Windows service that depends on third-party APIs:

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 the bindingRedirect tag in the app.config file, which works great when the service starts.

The problem is that the app.config file is not used when InstallUtil starts, so I get a binding exception when registering the service.

I am currently using "sc create" to manually register a service, but is there a better way?
(without editing machine.config, etc.)

+4
source share
2 answers

I just ran into this, the only solution I could find is https://connect.microsoft.com/VisualStudio/feedback/details/525564/installutil-exe-does-not-honor-app-config-especially-binding -information :

As a workaround, you can do this work by modifying the InstallUtil.exe.config file so that it contains the binding information. InstallUtil.exe.config is installed in% WinDir% \ Microsoft.NET \ Framework \\ InstallUtil.exe.config, which uses the framework version.

0
source

I came up with another workaround for installing the service with mandatory redirects. Since I have many services, this is what I decided to go after.

  1. Change the Windows installer to the console application and implement the functions for self-installation (using the command line and ManagedInstallerClass.InstallHelper).

  2. Implement an installer class that can execute the command line in a completely separate assembly, for example, CommandLineInstaller.DLL. CommandLineInstaller.DLL CommandLineInstaller.DLL. CommandLineInstaller.DLL should implement the Install / Uninstall / Rollback methods the same way - execute the command line with parameters such as: FileName, WorkingDirectory, Args, WindowStyle .

  3. Modify the installation project to deploy both 1) the service and b) CommandLineInstaller.DLL

  4. Change the custom actions of the installation project: instead of starting the service, start the CommandLineInstaller.DLL actions. The CustomActionData property for the Install action will look like this: /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 the installation project will launch the CommandLineInstaller.DLL installer in its own process. And then CommandLineInstaller.DLL, in turn, will start MyService.exe in its own process with blood binding redirects, as it should be.

PS MyService.exe can use the exit code mechanism to inform the installer about crashes, and I highly recommend checking them out in CommandLineInstaller.

Hope this is a pretty good sketch.

PS Remember that TARGETDIR must have a slash when it is passed to directories: /WorkDir="[TARGETDIR]\"

CustomActionData installation example: /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("\\\\", "\\"); } } } 
0
source

All Articles