C# .NET 8 Where can my Windows Service access the arguments from sc.exe start? - Stack Overflow

admin2025-04-17  2

In .NET Framework 4.6.2, you use this type of code for a Windows Service:

    public partial class ServiceMain : ServiceBase
    {
        public ServiceMain()
        {
            InitializeComponent();
        }

        protected override void OnStart( string[] args )
        {
            // I can do something with the args.

In .NET 8, every Windows Service example I've seen (and what I have written) is to have a Worker declared in your Main entry point (typically Program.cs)

        builder.Services.AddHostedService<Worker>();

and then that worker is just a background service:

public class Worker : BackgroundService

The args from Main are empty when starting via sc.exe. Is there some other way to grab those arguments?

I was expecting to see values in the Main args that were the args from the sc.exe call. I see no online examples of the .NET 8 equivalent of OnStart where there are service arguments.

This seems like an answer but I have confirmed that args are always empty.

In .NET Framework 4.6.2, you use this type of code for a Windows Service:

    public partial class ServiceMain : ServiceBase
    {
        public ServiceMain()
        {
            InitializeComponent();
        }

        protected override void OnStart( string[] args )
        {
            // I can do something with the args.

In .NET 8, every Windows Service example I've seen (and what I have written) is to have a Worker declared in your Main entry point (typically Program.cs)

        builder.Services.AddHostedService<Worker>();

and then that worker is just a background service:

public class Worker : BackgroundService

The args from Main are empty when starting via sc.exe. Is there some other way to grab those arguments?

I was expecting to see values in the Main args that were the args from the sc.exe call. I see no online examples of the .NET 8 equivalent of OnStart where there are service arguments.

This seems like an answer but I have confirmed that args are always empty.

Share Improve this question asked Feb 1 at 1:02 FoxScullyFoxScully 655 bronze badges 0
Add a comment  | 

1 Answer 1

Reset to default 2

I've created several asp core services myself and I thought you were mistaken - but you're correct, no accessible args.

So I checked the source: https://github.com/dotnet/runtime/tree/main/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/src

The WindowsServiceLifetime class has a virtual startup function that takes args.

protected override void OnStart(string[] args)

I couldn't find a public args member anywhere so I think the only way around this is to override this class and add your own as the implementation of IHostLifetime

I copied the following from WindowsServiceLifetimeHostBuilderExtensions.cs

I only made a small change to allow passing your own WindowsServiceLifetime based class.

namespace Microsoft.Extensions.Hosting
{

    public static class MyWindowsServiceLifetimeHostBuilderExtensions
    {
        public static void AddMyWindowsServiceLifetime<TImplementation>(this IServiceCollection services
            , Action<WindowsServiceLifetimeOptions> configure) where TImplementation : class, IHostLifetime
        {
            if (WindowsServiceHelpers.IsWindowsService())
            {

#if !NETFRAMEWORK
                Debug.Assert(RuntimeInformation.IsOSPlatform(OSPlatform.Windows));
#endif

                services.AddLogging(logging =>
                {
#if !NETFRAMEWORK
                    Debug.Assert(RuntimeInformation.IsOSPlatform(OSPlatform.Windows));
#endif
                    logging.AddEventLog();
                });
                services.AddSingleton<IHostLifetime, TImplementation>();
                services.AddSingleton<IConfigureOptions<EventLogSettings>, EventLogSettingsSetup>();
                services.Configure(configure);
            }
        }

        private sealed class EventLogSettingsSetup : IConfigureOptions<EventLogSettings>
        {
            private readonly string _applicationName;

            public EventLogSettingsSetup(IHostEnvironment environment)
            {
                _applicationName = environment.ApplicationName;
            }

            public void Configure(EventLogSettings settings)
            {
#if !NETFRAMEWORK
                Debug.Assert(RuntimeInformation.IsOSPlatform(OSPlatform.Windows));
#endif

                if (string.IsNullOrEmpty(settings.SourceName))
                {
                    settings.SourceName = _applicationName;
                }
            }
        }

    }
}

My own WindowsServiceLifetime based class looks like this:

[SupportedOSPlatform("windows")]
public class MyWindowsServiceLifetime : WindowsServiceLifetime
{
    public static string[] args { get; set; }
    public MyWindowsServiceLifetime(IHostEnvironment environment
        , IHostApplicationLifetime applicationLifetime
        , ILoggerFactory loggerFactory, IOptions<HostOptions> optionsAccessor)
        : base(environment, applicationLifetime, loggerFactory, optionsAccessor)
    {
    }
    protected override void OnStart(string[] args)
    {
        MyWindowsServiceLifetime.args = args;
        base.OnStart(args);
    }
}

In my startup code I call the new extension method like this:

 builder.Services.AddMyWindowsServiceLifetime<MyWindowsServiceLifetime>(c => { });

Now you can access the args from your own class, or in this case I made them public static.

var _args = MyWindowsServiceLifetime.args;
if (_args != null)
{
    foreach (var arg in _args)
    {
        logger.LogInformation("arg= {arg}", arg);
    }
}

The args are not available in the Program.cs startup code, but you can access them from anywhere else.

转载请注明原文地址:http://anycun.com/QandA/1744842408a88381.html