使用 Topshelf 设置服务启动参数
Set Service Start Parameter using Topshelf
我有一个服务有多个实例,每个实例都有不同的参数,目前我正在手动设置这些参数(准确地说是在另一个代码中)到注册表中服务的图像路径(例如HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\MyService$i00
).所以我们的服务安装分两步完成。
我真的很想在 Topshelf 安装中合并这些步骤,例如
MyService.exe install -instance "i00" -config "C:\i00Config.json"
第一次尝试
我从 TopShelf 尝试 AddCommandLineDefinition
,但它似乎只在安装期间有效,运行 通过控制台而不是服务本身(不会向服务图像路径添加任何内容)。
第二次尝试
我试着看看是否可以用来自 Topshelf 的 AfterInstall
来做到这一点,但没有任何运气。这是一个测试代码,看看它是否能正常工作(但不幸的是,Topshelf 在 AfterInstall
调用后覆盖了注册表)。
HostFactory.Run(x =>
{
x.UseNLog();
x.Service<MyService>(sc =>
{
sc.ConstructUsing(hs => new MyService(hs));
sc.WhenStarted((s, h) => s.Start(h));
sc.WhenStopped((s, h) => s.Stop(h));
});
x.AfterInstall(s =>
{
using (var system = Registry.LocalMachine.OpenSubKey("SYSTEM"))
using (var controlSet = system.OpenSubKey("CurrentControlSet"))
using (var services = controlSet.OpenSubKey("services"))
using (var service = services.OpenSubKey(string.IsNullOrEmpty(s.InstanceName)
? s.ServiceName
: s.ServiceName + "$" + s.InstanceName, true))
{
if (service == null)
return;
var imagePath = service.GetValue("ImagePath") as string;
if (string.IsNullOrEmpty(imagePath))
return;
var appendix = string.Format(" -{0} \"{1}\"", "config", "C:\i00config.json"); //only a test to see if it is possible at all or not
imagePath = imagePath + appendix;
service.SetValue("ImagePath", imagePath);
}
});
x.SetServiceName("MyService");
x.SetDisplayName("My Service");
x.SetDescription("My Service Sample");
x.StartAutomatically();
x.RunAsLocalSystem();
x.EnableServiceRecovery(r =>
{
r.OnCrashOnly();
r.RestartService(1); //first
r.RestartService(1); //second
r.RestartService(1); //subsequents
r.SetResetPeriod(0);
});
});
我找不到任何有关如何使用 TopShelf 完成此操作的相关信息,所以问题是,是否可以使用 TopShelf 来完成此操作?
为了回答您的问题,不,Topshelf 不可能做到这一点。我很高兴您知道如何管理 ImagePath。但这就是问题的症结所在,过去在邮件列表 (https://groups.google.com/d/msg/topshelf-discuss/Xu4XR6wGWxw/8mAtyJFATq8J) 上就此主题和相关问题进行了一些讨论。
最大的问题是,在将自定义参数应用于 ImagePath 时,管理行为预期是不直观的。例如,当您使用自定义命令行参数调用 start 时会发生什么?如果我们得到的东西不会让我在思考时感到困惑,更不用说尝试使用了,我愿意实施这个或接受 PR。现在,我强烈建议您使用配置而不是命令行参数来管理它,即使这意味着在磁盘上复制代码。
好的,正如 Travis 提到的,似乎没有针对此问题的内置功能或简单的解决方法。所以我基于自定义环境构建器为 Topshelf 编写了一个小扩展(大部分代码是从 Topshelf 项目本身借来的)。
我在 Github 上发布了代码,以防其他人发现它有用,这里是 Topshelf.StartParameters 扩展。
基于扩展我的代码如下:
HostFactory.Run(x =>
{
x.EnableStartParameters();
x.UseNLog();
x.Service<MyService>(sc =>
{
sc.ConstructUsing(hs => new MyService(hs));
sc.WhenStarted((s, h) => s.Start(h));
sc.WhenStopped((s, h) => s.Stop(h));
});
x.WithStartParameter("config",a =>{/*we can use parameter here*/});
x.SetServiceName("MyService");
x.SetDisplayName("My Service");
x.SetDescription("My Service Sample");
x.StartAutomatically();
x.RunAsLocalSystem();
x.EnableServiceRecovery(r =>
{
r.OnCrashOnly();
r.RestartService(1); //first
r.RestartService(1); //second
r.RestartService(1); //subsequents
r.SetResetPeriod(0);
});
});
我可以简单地设置它:
MyService.exe install -instance "i00" -config "C:\i00Config.json"
以下解决方法只不过是注册表更新。更新操作需要安装程序写入扩展参数所需的权限。
基本上,我们正在响应 AfterInstall()
事件。从 Topshelf v4.0.3 开始,从事件中调用 AppendImageArgs()
变通方法将导致您的参数出现 在 TS 参数之前。如果延迟调用,您的参数将出现在 TS 参数之后。
解决方法
private static void AppendImageArgs(string serviceName, IEnumerable<Tuple<string, object>> args)
{
try
{
using (var service = Registry.LocalMachine.OpenSubKey($@"System\CurrentControlSet\Services\{serviceName}", true))
{
const string imagePath = "ImagePath";
var value = service?.GetValue(imagePath) as string;
if (value == null)
return;
foreach (var arg in args)
if (arg.Item2 == null)
value += $" -{arg.Item1}";
else
value += $" -{arg.Item1} \"{arg.Item2}\"";
service.SetValue(imagePath, value);
}
}
catch (Exception e)
{
Log.Error(e);
}
}
调用示例[=40=]
private static void AppendImageArgs(string serviceName)
{
var args = new[]
{
new Tuple<string, object>("param1", "Hello"),
new Tuple<string, object>("param2", 1),
new Tuple<string, object>("Color", ConsoleColor.Cyan),
};
AppendImageArgs(serviceName, args);
}
以及将出现在 ImagePath 中的结果参数:
-displayname "MyService Display Name" -servicename "MyServiceName" -param1 "Hello" -param2 "1" -Color "Cyan"
注意 args 出现在 TS args 之后,-displayname
& -servicename
。在此示例中,AppendImageArgs()
调用是在 TS 完成其安装业务后调用的。
命令行参数可以使用Topshelf方法正常指定,例如AddCommandLineDefinition()
。要强制处理参数,请调用 ApplyCommandLine()
.
我有一个服务有多个实例,每个实例都有不同的参数,目前我正在手动设置这些参数(准确地说是在另一个代码中)到注册表中服务的图像路径(例如HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\MyService$i00
).所以我们的服务安装分两步完成。
我真的很想在 Topshelf 安装中合并这些步骤,例如
MyService.exe install -instance "i00" -config "C:\i00Config.json"
第一次尝试
我从 TopShelf 尝试 AddCommandLineDefinition
,但它似乎只在安装期间有效,运行 通过控制台而不是服务本身(不会向服务图像路径添加任何内容)。
第二次尝试
我试着看看是否可以用来自 Topshelf 的 AfterInstall
来做到这一点,但没有任何运气。这是一个测试代码,看看它是否能正常工作(但不幸的是,Topshelf 在 AfterInstall
调用后覆盖了注册表)。
HostFactory.Run(x =>
{
x.UseNLog();
x.Service<MyService>(sc =>
{
sc.ConstructUsing(hs => new MyService(hs));
sc.WhenStarted((s, h) => s.Start(h));
sc.WhenStopped((s, h) => s.Stop(h));
});
x.AfterInstall(s =>
{
using (var system = Registry.LocalMachine.OpenSubKey("SYSTEM"))
using (var controlSet = system.OpenSubKey("CurrentControlSet"))
using (var services = controlSet.OpenSubKey("services"))
using (var service = services.OpenSubKey(string.IsNullOrEmpty(s.InstanceName)
? s.ServiceName
: s.ServiceName + "$" + s.InstanceName, true))
{
if (service == null)
return;
var imagePath = service.GetValue("ImagePath") as string;
if (string.IsNullOrEmpty(imagePath))
return;
var appendix = string.Format(" -{0} \"{1}\"", "config", "C:\i00config.json"); //only a test to see if it is possible at all or not
imagePath = imagePath + appendix;
service.SetValue("ImagePath", imagePath);
}
});
x.SetServiceName("MyService");
x.SetDisplayName("My Service");
x.SetDescription("My Service Sample");
x.StartAutomatically();
x.RunAsLocalSystem();
x.EnableServiceRecovery(r =>
{
r.OnCrashOnly();
r.RestartService(1); //first
r.RestartService(1); //second
r.RestartService(1); //subsequents
r.SetResetPeriod(0);
});
});
我找不到任何有关如何使用 TopShelf 完成此操作的相关信息,所以问题是,是否可以使用 TopShelf 来完成此操作?
为了回答您的问题,不,Topshelf 不可能做到这一点。我很高兴您知道如何管理 ImagePath。但这就是问题的症结所在,过去在邮件列表 (https://groups.google.com/d/msg/topshelf-discuss/Xu4XR6wGWxw/8mAtyJFATq8J) 上就此主题和相关问题进行了一些讨论。
最大的问题是,在将自定义参数应用于 ImagePath 时,管理行为预期是不直观的。例如,当您使用自定义命令行参数调用 start 时会发生什么?如果我们得到的东西不会让我在思考时感到困惑,更不用说尝试使用了,我愿意实施这个或接受 PR。现在,我强烈建议您使用配置而不是命令行参数来管理它,即使这意味着在磁盘上复制代码。
好的,正如 Travis 提到的,似乎没有针对此问题的内置功能或简单的解决方法。所以我基于自定义环境构建器为 Topshelf 编写了一个小扩展(大部分代码是从 Topshelf 项目本身借来的)。
我在 Github 上发布了代码,以防其他人发现它有用,这里是 Topshelf.StartParameters 扩展。
基于扩展我的代码如下:
HostFactory.Run(x =>
{
x.EnableStartParameters();
x.UseNLog();
x.Service<MyService>(sc =>
{
sc.ConstructUsing(hs => new MyService(hs));
sc.WhenStarted((s, h) => s.Start(h));
sc.WhenStopped((s, h) => s.Stop(h));
});
x.WithStartParameter("config",a =>{/*we can use parameter here*/});
x.SetServiceName("MyService");
x.SetDisplayName("My Service");
x.SetDescription("My Service Sample");
x.StartAutomatically();
x.RunAsLocalSystem();
x.EnableServiceRecovery(r =>
{
r.OnCrashOnly();
r.RestartService(1); //first
r.RestartService(1); //second
r.RestartService(1); //subsequents
r.SetResetPeriod(0);
});
});
我可以简单地设置它:
MyService.exe install -instance "i00" -config "C:\i00Config.json"
以下解决方法只不过是注册表更新。更新操作需要安装程序写入扩展参数所需的权限。
基本上,我们正在响应 AfterInstall()
事件。从 Topshelf v4.0.3 开始,从事件中调用 AppendImageArgs()
变通方法将导致您的参数出现 在 TS 参数之前。如果延迟调用,您的参数将出现在 TS 参数之后。
解决方法
private static void AppendImageArgs(string serviceName, IEnumerable<Tuple<string, object>> args)
{
try
{
using (var service = Registry.LocalMachine.OpenSubKey($@"System\CurrentControlSet\Services\{serviceName}", true))
{
const string imagePath = "ImagePath";
var value = service?.GetValue(imagePath) as string;
if (value == null)
return;
foreach (var arg in args)
if (arg.Item2 == null)
value += $" -{arg.Item1}";
else
value += $" -{arg.Item1} \"{arg.Item2}\"";
service.SetValue(imagePath, value);
}
}
catch (Exception e)
{
Log.Error(e);
}
}
调用示例[=40=]
private static void AppendImageArgs(string serviceName)
{
var args = new[]
{
new Tuple<string, object>("param1", "Hello"),
new Tuple<string, object>("param2", 1),
new Tuple<string, object>("Color", ConsoleColor.Cyan),
};
AppendImageArgs(serviceName, args);
}
以及将出现在 ImagePath 中的结果参数:
-displayname "MyService Display Name" -servicename "MyServiceName" -param1 "Hello" -param2 "1" -Color "Cyan"
注意 args 出现在 TS args 之后,-displayname
& -servicename
。在此示例中,AppendImageArgs()
调用是在 TS 完成其安装业务后调用的。
命令行参数可以使用Topshelf方法正常指定,例如AddCommandLineDefinition()
。要强制处理参数,请调用 ApplyCommandLine()
.