简体   繁体   English

使用Topshelf设置服务启动参数

[英]Set Service Start Parameter using Topshelf

I have a service with multiple instances with different parameters for each instance, at the moment I'm setting these parameters manually (in another code to be exact) to Image Path of the service in Registry (eg HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\services\\MyService$i00 ). 我有一个服务,每个实例具有不同参数的多个实例,目前我手动设置这些参数(准确地说是另一个代码)到Registry中服务的Image Path(例如HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\services\\MyService$i00 )。 so our service installation is done in two steps. 所以我们的服务安装分两步完成。

I'm really interested to merge these steps in Topshelf installation for example like 我真的很想在Topshelf安装中合并这些步骤,例如

MyService.exe install -instance "i00" -config "C:\i00Config.json"

First Try 第一次尝试

I tried AddCommandLineDefinition from TopShelf but it seems it only works during installation and running through console not the service itself (will not add anything to service Image Path). 我试图AddCommandLineDefinition从TopShelf但现在看来,这只能通过控制台安装和运行过程中的工作不是服务本身(不添加任何服务映像路径)。

Second Try 第二次尝试

I tried to see if its possible to do this with AfterInstall from Topshelf without any luck. 我试图看看它是否有可能用Topshelf的AfterInstall做到这一点而没有任何运气。 here is a test code to see if it going to work or not (but unfortunately Topshelf overwrites the registry after AfterInstall call). 这是一个测试代码,看看它是否能够工作(但不幸的是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);
            });
        });

I couldn't find any relevant information about how it can be done using TopShelf so the question is, is it possible to do this with TopShelf? 我找不到任何关于如何使用TopShelf完成任何相关信息所以问题是,是否可以使用TopShelf执行此操作?

To answer you question, no this isn't possible with Topshelf. 为了回答你的问题,Topshelf不可能做到这一点。 I am excited you figured out how to manage the ImagePath. 我很兴奋你想出了如何管理ImagePath。 But that's the crux of the problem, there's been some discussion on the mailing list ( https://groups.google.com/d/msg/topshelf-discuss/Xu4XR6wGWxw/8mAtyJFATq8J ) on this topic and issues about it in the past. 但这就是问题的症结所在,关于这个主题的邮件列表( https://groups.google.com/d/msg/topshelf-discuss/Xu4XR6wGWxw/8mAtyJFATq8J )已经进行了一些讨论,并且过去就此问题进行了讨论。

The big problem is that managing expectations of behavior when applying custom arguments to the ImagePath will be unintuitive. 最大的问题是,在将自定义参数应用于ImagePath时管理行为期望将是不直观的。 For example, what happens when you call start with custom command line arguments? 例如,当您使用自定义命令行参数调用start时会发生什么? I'm open to implementing this or accepting a PR if we get something that doesn't confuse me just thinking about it, let alone trying to use. 如果我们得到的东西不会让我感到困惑,我会乐于实现这个或接受PR,更不用说尝试使用了。 Right now, I strongly encourage you to use configuration, not command line arguments, to manage this, even if it means duplicating code on disk. 现在,我强烈建议您使用配置而不是命令行参数来管理它,即使它意味着复制磁盘上的代码。

Ok, as Travis mentioned, It seems there is no built-in feature or simple workaround for this problem. 好吧,正如Travis所说,似乎没有针对此问题的内置功能或简单的解决方法。 so I wrote a little extension for Topshelf based on a Custom Environment Builder (most of the code is borrowed form Topshelf project itself). 所以我基于Custom Environment Builder为Topshelf编写了一个小扩展(大部分代码都是从Topshelf项目本身借来的)。

I posted the code on Github, in case others may find it useful, here is the Topshelf.StartParameters extension. 我在Github上发布了代码,以防其他人发现它有用,这里是Topshelf.StartParameters扩展。

based on the extension my code would be like: 基于扩展我的代码将是:

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);
        });
    });

and I can simply set it with: 我可以简单地设置它:

MyService.exe install -instance "i00" -config "C:\i00Config.json"

The following work-around is nothing more than a registry update. 以下解决方法只是注册表更新。 The update operation expects the privileges the installer requires in order to write our extended arguments. 更新操作需要安装程序为编写扩展参数而需要的权限。

Basically, we're responding to the AfterInstall() event. 基本上,我们正在响应AfterInstall()事件。 As of Topshelf v4.0.3, calling the AppendImageArgs() work-around from within the event will cause your args to appear before the TS args. 从Topshelf v4.0.3开始,在事件中调用AppendImageArgs()将导致你的args出现 TS args 之前 If the call is deferred, your args will appear after the TS args. 如果延迟调用,您的args将出现 TS args之后。

The work-around 解决方法

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);
  }
}

An example call 一个示例电话

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);
}

And the resulting args that would appear in the ImagePath: 以及将出现在ImagePath中的结果args:

 -displayname "MyService Display Name" -servicename "MyServiceName" -param1 "Hello" -param2 "1" -Color "Cyan"

Notice the args appeared after the TS args, -displayname & -servicename . 请注意,args出现在TS args, -displayname-servicename In this example, the AppendImageArgs() call was invoked after TS finished its installation business. 在此示例中,在TS完成其安装业务后调用AppendImageArgs()调用。

Command line args can be specified normally using Topshelf methods such as AddCommandLineDefinition() . 可以使用Topshelf方法(如AddCommandLineDefinition()正常指定命令行参数。 To force processing of the args, call ApplyCommandLine() . 要强制处理args,请调用ApplyCommandLine()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM