【问题标题】:Create windows service that takes start parameters创建带有启动参数的 Windows 服务
【发布时间】:2016-05-24 14:15:09
【问题描述】:

我已经使用TOPSHELF dlls创建了一个windows服务,但我想让它在安装后能够接收参数。

假设我的folderpath 是服务读取文件的位置。

这是我的 Start 方法。

public void Start()
        {
            _log.Info("SampleService is Started");

            _timer.AutoReset = true;
            _timer.Enabled = true;
            _timer.Start();

            //String folderpath I want to use this as a start parameter.
            GetAndConvertFileIntoXML(folderPath);

        }

【问题讨论】:

  • 我应该使用 String folderpath=Console.ReadLine();这里?它会起作用吗?
  • 好吧,因为服务没有控制台,那将无法工作-您的程序接收参数..如果您需要将它们传递给服务-然后查看您的 program.cs 并使其成为您的服务创建有参数,或调用方法,或任何你需要的......
  • 您不能再使用标准方式启动服务。它不能自动启动,不能使用 Windows 服务管理器或 NET 命令。您必须编写另一个程序,例如使用 GUI 来获取参数值并使用 ServiceController.Start(string[]) 重载。这很少做,不实用。

标签: c# .net windows-services console-application topshelf


【解决方案1】:

Oliver 有一个 great answer 如果你想走那条路。这不是我希望在 Topshelf 中看到的东西(即使补丁出现在邮件中)。

我们没有办法在 Topshelf 中执行此操作,因为理解您使用的上下文很复杂。我们认为您应该使用 app.config 而不是命令行参数来管理它。

让我解释一下我没有弄清楚的体验问题:service install --myparam=one - 太好了!我们像 Oliver 的补丁一样修改IMAGE_PATH。现在,如果您只使用service install 安装更新应该是什么?我们是否应该尝试保留该参数?如果它与实例名称相关联而您忘记了怎么办?此外,无法查看为给定服务设置了哪些参数。

如果有人帮助我想出一种方法让用户清楚地了解这种体验,我会倾向于将它与 Topshelf 一起发布。同时,我们的指导是使用 app.config。

【讨论】:

    【解决方案2】:

    当您使用命令行手动安装 Windows 服务时,您可以提供应添加到应用程序轻松启动的附加参数:

    sc create MyService binPath="notepad d:\myFile.txt"
    

    很遗憾,Topshelf 不支持这一点。因此,我获取了资源并自己添加了该功能。我向项目发送了所需的补丁(不是通过拉取请求,而是通过邮件),但他们从未回复我或将功能构建到代码中。

    应用该补丁(见底部)后,您将获得一个新方法SetArguments(),它让我们定义您的服务应附加到服务启动时调用的 exe 路径的附加参数。如果您想在使用 install 关键字调用应用程序时也将此参数提供给应用程序,那么它会变得有点复杂,但它可以工作。首先是将所需的命令行参数转发给服务安装程序所需的代码:

    var host = HostFactory.New(x =>
    {
        x.AddCommandLineDefinition("configuration", filename =>
        {
            configFileName = CreateFullPathName(filename);
            x.SetArguments("-configuration \"" + configFileName + "\"");
        });
    
        ...
    });
    

    如果你现在用命令行调用你的程序

    MyServiceApplication install -configuration ".\Some Sub Folder\My File.txt"
    

    您可以在 services.msc 中看到,在您的服务的可执行文件路径下,该参数已被添加,并将在服务启动时填充。

    补丁文件

    Index: src/Topshelf/Runtime/HostSettings.cs
    ===================================================================
    --- src/Topshelf/Runtime/HostSettings.cs    (original)
    +++ src/Topshelf/Runtime/HostSettings.cs    (add SetArguments)
    @@ -38,6 +38,11 @@
             string InstanceName { get; }
    
             /// <summary>
    +        ///   The additional arguments that should be appended when the service is installed
    +        /// </summary>
    +        string Arguments { get; }
    +
    +        /// <summary>
             ///   Returns the Windows service name, including the instance name, which is registered with the SCM Example: myservice$bob
             /// </summary>
             /// <returns> </returns>
    Index: src/Topshelf/Runtime/Windows/HostServiceInstaller.cs
    ===================================================================
    --- src/Topshelf/Runtime/Windows/HostServiceInstaller.cs    (original)
    +++ src/Topshelf/Runtime/Windows/HostServiceInstaller.cs    (add SetArguments)
    @@ -114,6 +114,9 @@
                 if (!string.IsNullOrEmpty(settings.Name))
                     arguments += string.Format(" -servicename \"{0}\"", settings.Name);
    
    +            if (!string.IsNullOrEmpty(settings.Arguments))
    +                arguments += " " + settings.Arguments;
    +
                 return new HostInstaller(settings, arguments, installers);
             }
    
    Index: src/Topshelf/Runtime/Windows/WindowsHostSettings.cs
    ===================================================================
    --- src/Topshelf/Runtime/Windows/WindowsHostSettings.cs (original)
    +++ src/Topshelf/Runtime/Windows/WindowsHostSettings.cs (add SetArguments)
    @@ -1,14 +1,14 @@
     // Copyright 2007-2012 Chris Patterson, Dru Sellers, Travis Smith, et. al.
    -//  
    -// Licensed under the Apache License, Version 2.0 (the "License"); you may not use 
    -// this file except in compliance with the License. You may obtain a copy of the 
    -// License at 
    -// 
    -//     http://www.apache.org/licenses/LICENSE-2.0 
    -// 
    -// Unless required by applicable law or agreed to in writing, software distributed 
    -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
    -// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
    +// this file except in compliance with the License. You may obtain a copy of the
    +// License at
    +//
    +//     http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software distributed
    +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
    +// CONDITIONS OF ANY KIND, either express or implied. See the License for the
     // specific language governing permissions and limitations under the License.
     namespace Topshelf.Runtime.Windows
     {
    @@ -19,8 +19,8 @@
             HostSettings
         {
             public const string InstanceSeparator = "$";
    -        string _description;
    -        string _displayName;
    +        private string _description;
    +        private string _displayName;
    
             /// <summary>
             ///   Creates a new WindowsServiceDescription using empty strings for the properties. The class is required to have names by the consumers.
    @@ -49,6 +49,8 @@
                 _description = "";
             }
    
    +        public string Arguments { get; set; }
    +
             public string Name { get; set; }
    
             public string DisplayName
    @@ -68,7 +70,6 @@
                 set { _displayName = value; }
             }
    
    -
             public string Description
             {
                 get
    @@ -80,7 +81,6 @@
                 set { _description = value; }
             }
    
    -
             public string InstanceName { get; set; }
    
             public string ServiceName
    Index: src/Topshelf/Topshelf.csproj
    ===================================================================
    --- src/Topshelf/Topshelf.csproj    (original)
    +++ src/Topshelf/Topshelf.csproj    (add SetArguments)
    @@ -112,6 +112,7 @@
         <Compile Include="Configuration\HostConfigurators\SudoConfigurator.cs" />
         <Compile Include="Configuration\HostConfigurators\UninstallHostConfiguratorAction.cs" />
         <Compile Include="Configuration\HostConfigurators\UnknownCommandLineOptionHostConfigurator.cs" />
    +    <Compile Include="Configuration\Options\ArgumentsOption.cs" />
         <Compile Include="Configuration\Options\AutostartOption.cs" />
         <Compile Include="Configuration\Options\DelayedOption.cs" />
         <Compile Include="Configuration\Options\DisabledOption.cs" />
    @@ -221,6 +222,9 @@
       <ItemGroup>
         <Folder Include="Properties\" />
       </ItemGroup>
    +  <ItemGroup>
    +    <None Include="Internals\README.md" />
    +  </ItemGroup>
       <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
       <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
            Other similar extension points exist, see Microsoft.Common.targets.
    Index: src/Topshelf/Hosts/InstallHost.cs
    ===================================================================
    --- src/Topshelf/Hosts/InstallHost.cs   (original)
    +++ src/Topshelf/Hosts/InstallHost.cs   (add SetArguments)
    @@ -139,6 +139,11 @@
                     _dependencies = dependencies;
                 }
    
    +            public string Arguments
    +            {
    +                get { return _settings.Arguments; }
    +            }
    +
                 public string Name
                 {
                     get { return _settings.Name; }
    Index: src/Topshelf/Configuration/HostConfigurators/CommandLineParserOptions.cs
    ===================================================================
    --- src/Topshelf/Configuration/HostConfigurators/CommandLineParserOptions.cs    (original)
    +++ src/Topshelf/Configuration/HostConfigurators/CommandLineParserOptions.cs    (add SetArguments)
    @@ -61,7 +61,9 @@
                     .Or(from disp in x.Definition("displayname")
                         select (Option)new DisplayNameOption(disp.Value))
                     .Or(from instance in x.Definition("instance")
    -                    select (Option)new InstanceOption(instance.Value)));
    +                    select (Option)new InstanceOption(instance.Value))
    +                .Or(from instance in x.Definition("arguments")
    +                    select (Option)new ArgumentsOption(instance.Value)));
             }
    
             internal static void AddUnknownOptions(ICommandLineElementParser<Option> x)
    Index: src/Topshelf/Configuration/HostConfigurators/HostConfiguratorImpl.cs
    ===================================================================
    --- src/Topshelf/Configuration/HostConfigurators/HostConfiguratorImpl.cs    (original)
    +++ src/Topshelf/Configuration/HostConfigurators/HostConfiguratorImpl.cs    (add SetArguments)
    @@ -64,7 +64,7 @@
                     yield return this.Failure("Name", "must be specified and not empty");
                 else
                 {
    -                var disallowed = new[] {' ', '\t', '\r', '\n', '\\', '/'};
    +                var disallowed = new[] { ' ', '\t', '\r', '\n', '\\', '/' };
                     if (_settings.Name.IndexOfAny(disallowed) >= 0)
                         yield return this.Failure("Name", "must not contain whitespace, '/', or '\\' characters");
                 }
    @@ -86,6 +86,11 @@
                 yield return this.Success("ServiceName", _settings.ServiceName);
             }
    
    +        public void SetArguments(string arguments)
    +        {
    +            _settings.Arguments = arguments;
    +        }
    +
             public void SetDisplayName(string name)
             {
                 _settings.DisplayName = name;
    Index: src/Topshelf/Configuration/HostConfigurators/HostConfigurator.cs
    ===================================================================
    --- src/Topshelf/Configuration/HostConfigurators/HostConfigurator.cs    (original)
    +++ src/Topshelf/Configuration/HostConfigurators/HostConfigurator.cs    (add SetArguments)
    @@ -17,6 +17,12 @@
         public interface HostConfigurator
         {
             /// <summary>
    +        ///   Specifies additional command line arguments that should be added when the service is registered
    +        /// </summary>
    +        /// <param name="name"> </param>
    +        void SetArguments(string arguments);
    +
    +        /// <summary>
             ///   Specifies the name of the service as it should be displayed in the service control manager
             /// </summary>
             /// <param name="name"> </param>
    Index: src/Topshelf/Configuration/Options/ArgumentsOption.cs
    ===================================================================
    --- src/Topshelf/Configuration/Options/ArgumentsOption.cs   (nonexistent)
    +++ src/Topshelf/Configuration/Options/ArgumentsOption.cs   (add SetArguments)
    @@ -0,0 +1,31 @@
    +// Copyright 2007-2012 Chris Patterson, Dru Sellers, Travis Smith, et. al.
    +//  
    +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use 
    +// this file except in compliance with the License. You may obtain a copy of the 
    +// License at 
    +// 
    +//     http://www.apache.org/licenses/LICENSE-2.0 
    +// 
    +// Unless required by applicable law or agreed to in writing, software distributed 
    +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
    +// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
    +// specific language governing permissions and limitations under the License.
    +namespace Topshelf.Options
    +{
    +    using HostConfigurators;
    +
    +    public class ArgumentsOption : Option
    +    {
    +        string _arguments;
    +
    +        public ArgumentsOption(string arguments)
    +        {
    +            _arguments = arguments;
    +        }
    +
    +        public void ApplyTo(HostConfigurator configurator)
    +        {
    +            configurator.SetArguments(_arguments);
    +        }
    +    }
    +}
    \ No newline at end of file
    Index: src/Topshelf/HelpText.txt
    ===================================================================
    --- src/Topshelf/HelpText.txt   (original)
    +++ src/Topshelf/HelpText.txt   (add SetArguments)
    @@ -28,8 +28,10 @@
                             installing
           -description      The service description the service should use when
                             installing
    -      -displayname      The display name the the service should use when
    +      -displayname      The display name the service should use when
                             installing
    +      -arguments        The command line arguments the service should
    +                        also get when installing
    
         start               Starts the service if it is not already running
    
    @@ -57,3 +59,10 @@
             Installs the service, appending the instance name to the service name
             so that the service can be installed multiple times. You may need to
             tweak the log4net.config to make this play nicely with the log files.
    +
    +    service install -arguments "-configFile \"C:\my folder\service.config\""
    +        Installs the service, appending the command line argument to the
    +        service so that the service will always retrieve this information
    +        when started.
    +        If double quotes are needed within the arguments they must be
    +        escaped by a backslash \" (see example).
    

    【讨论】:

    • 谢谢奥利弗!我想知道如何将启动参数传递给 topshelf 中的服务。
    【解决方案3】:

    你有非常简单的选择

    static void Main(String[] args)
    {
       ServiceBase[] ServicesToRun;
       ServicesToRun = new ServiceBase[] { new Service1(args) };
       ServiceBase.Run(ServicesToRun);
    }
    

    然后

    public Service1(String[] args)
    {
      if (args.Length==0) // terminate with appropriate error
      else
          folderPath = arg[0]; // as an example
    }
    

    然后在您的服务设置中,您可以/必须包含路径。 或者,只需从 exe 所在的设置文件中读取它 - 这是另一种正常的执行方式。

    【讨论】:

      猜你喜欢
      • 2011-06-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多