リダイレクトをバインドしてサービスをインストールする別の回避策を思いつきました。たくさんのサービスがあるので、これに決めました。
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 /uninstall アンインストール: myservice /uninstall
コミットを書く必要はありません、AFAIK。
これで、セットアップ プロジェクトは独自のプロセスで CommandLineInstaller.DLL インストーラーを実行します。次に、CommandLineInstaller.DLL は、本来あるべき血まみれのバインド リダイレクトを使用して、独自のプロセスで MyService.exe を起動します。
PSMyService.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("\\\\", "\\");
}
}
}