簡體   English   中英

使用Topshelf設置服務啟動參數

[英]Set Service Start Parameter using Topshelf

我有一個服務,每個實例具有不同參數的多個實例,目前我手動設置這些參數(准確地說是另一個代碼)到Registry中服務的Image Path(例如HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\services\\MyService$i00 )。 所以我們的服務安裝分兩步完成。

我真的很想在Topshelf安裝中合並這些步驟,例如

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

第一次嘗試

我試圖AddCommandLineDefinition從TopShelf但現在看來,這只能通過控制台安裝和運行過程中的工作不是服務本身(不添加任何服務映像路徑)。

第二次嘗試

我試圖看看它是否有可能用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所說,似乎沒有針對此問題的內置功能或簡單的解決方法。 所以我基於Custom Environment Builder為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()將導致你的args出現 TS args 之前 如果延遲調用,您的args將出現 TS args之后。

解決方法

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

一個示例電話

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中的結果args:

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

請注意,args出現在TS args, -displayname-servicename 在此示例中,在TS完成其安裝業務后調用AppendImageArgs()調用。

可以使用Topshelf方法(如AddCommandLineDefinition()正常指定命令行參數。 要強制處理args,請調用ApplyCommandLine()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM