我有一个依赖于3:party party API的Windows服务
API已安装在客户端计算机上的GAC中
有几个版本的API(1.0.0.0,1.1.0.0等)
我的服务适用于所有版本的API
我在app.config文件中使用bindingRedirect标记,在运行服务时可以正常工作。
问题是运行InstallUtil时未使用app.config文件,因此在注册服务时出现绑定异常。
目前我使用“sc create”手动注册服务但有更好的方法吗? (不编辑machine.config等)
答案 0 :(得分:0)
我刚刚参与其中,我能找到的唯一解决方案来自https://connect.microsoft.com/VisualStudio/feedback/details/525564/installutil-exe-does-not-honor-app-config-especially-binding-information:
作为一种变通方法,您可以通过修改InstallUtil.exe.config文件来包含绑定信息来完成此工作。 InstallUtil.exe.config安装到%WinDir%\ Microsoft.NET \ Framework \\ InstallUtil.exe.config,其中是您正在使用的框架版本。
答案 1 :(得分:0)
我想出了另一种解决方法来安装具有绑定重定向的服务。由于我有很多服务,这就是我决定要追求的。
将Windows安装程序更改为控制台应用程序,并实现自安装功能(使用命令行和ManagedInstallerClass.InstallHelper)。
实施一个能够在完全独立的程序集中执行命令行的安装程序类,例如CommandLineInstaller.DLL. CommandLineInstaller.DLL
应该以相同的方式实现Install / Uninstall / Rollback方法-使用以下参数执行命令行:
FileName, WorkingDirectory, Args, WindowStyle
。
修改设置项目以部署1)服务和b)CommandLineInstaller.DLL
修改安装程序项目自定义操作:运行CommandLineInstaller.DLL操作,而不是运行服务操作。 Install操作的CustomActionData属性如下所示:
/FileName="[TARGETDIR]MyService.exe" /Args="/install" WindowStyle="Hidden"
动作配置: 安装:myservice / install 回滚:myservice /卸载 卸载:myservice / uninstall
无需编写Commit,AFAIK。
现在,安装程序项目将在其自己的进程中执行CommandLineInstaller.DLL安装程序。然后,CommandLineInstaller.DLL将依次在自己的进程中启动MyService.exe,并执行带有血腥的绑定重定向。
PS MyService.exe
可以使用退出代码机制来通知安装程序失败,我强烈建议从CommandLineInstaller进行检查。
希望这是一个足够好的轮廓。
PS请注意,TARGETDIR自身传递到目录时需要加斜杠:
/WorkDir="[TARGETDIR]\"
安装CustomActionData的示例:
/FileName="[TARGETDIR]\MyService.exe" /Args="/install" /WorkingDir="[TARGETDIR]\" /ValidExitCode="0" /WindowStyle="Normal"
某些代码:
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("\\\\", "\\");
}
}
}