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.
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.