-
Notifications
You must be signed in to change notification settings - Fork 57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fixed typos and capitalization and improved XML doc comments #78
Conversation
Thanks! Out of interest: which part are you using? managing services / service host / both? |
@dasMulli Both for sure. I'm still getting acquainted with it. I want my entire service definition to look something like this: using System.Threading;
using System.Threading.Tasks;
public sealed class Program
{
public static Task<int> Main(string[] args)
{
return StandardServiceApp.MainAsync(args, RunAsync, serviceName: typeof(Program).Assembly.GetAssemblyTitle());
}
public static async Task RunAsync(CancellationToken stopToken)
{
while (true)
{
await Task.Delay(2000, stopToken).ConfigureAwait(false);
System.IO.File.WriteAllText(@"C:\Users\Joseph\Desktop\Test.txt", System.DateTime.Now.ToString());
}
}
} My current WIP implementation, using Microsoft's new System.CommandLine to handle the CLI argument parsing and to inject a CancellationToken that responds to Ctrl+C when run interactively: using DasMulli.Win32.ServiceUtils;
using System;
using System.CommandLine;
using System.CommandLine.Invocation;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
public static class StandardServiceApp
{
public static Task<int> MainAsync(string[] args, Func<CancellationToken, Task> runAsync, string serviceName)
{
if (runAsync is null)
throw new ArgumentNullException(nameof(runAsync));
if (string.IsNullOrWhiteSpace(serviceName))
throw new ArgumentException("Service name must be specified.", nameof(serviceName));
var root = new RootCommand(serviceName, handler: CreateServiceRunCommandHandler(runAsync, serviceName))
{
new Command("run", "Runs the service interactively in the current console window.", handler: CreateInteractiveRunCommandHandler(runAsync)),
new Command("install", "Installs the service as a Windows service.", handler: CreateInstallCommandHandler(serviceName)),
new Command("uninstall", "Uninstalls the service as a Windows service.", handler: CreateUninstallCommandHandler(serviceName))
};
return root.InvokeAsync(args);
}
private static ICommandHandler CreateServiceRunCommandHandler(Func<CancellationToken, Task> runAsync, string serviceName)
{
return CommandHandler.Create((IConsole console) =>
{
const int ERROR_FAILED_SERVICE_CONTROLLER_CONNECT = 1063;
try
{
return new Win32ServiceHost(new Service(runAsync, serviceName)).Run();
}
catch (Win32Exception ex) when (ex.NativeErrorCode == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)
{
console.Out.WriteLine("Error: " + ex.Message);
console.Out.WriteLine("Use --help to see available commands, such as running the service interactively in the current console window.");
return 1;
}
});
}
private static ICommandHandler CreateInteractiveRunCommandHandler(Func<CancellationToken, Task> runAsync)
{
return CommandHandler.Create(async (IConsole console, CancellationToken cancellationToken) =>
{
console.Out.WriteLine("Service is running interactively. Press Ctrl+C to stop.");
await runAsync.Invoke(cancellationToken).TreatCancellationAsSuccess();
if (cancellationToken.IsCancellationRequested)
console.Out.WriteLine("Stopped in response to Ctrl+C.");
});
}
private static ICommandHandler CreateInstallCommandHandler(string serviceName)
{
return CommandHandler.Create((IConsole console) =>
{
throw new NotImplementedException();
console.Out.WriteLine("The service was successfully added.");
});
}
private static ICommandHandler CreateUninstallCommandHandler(string serviceName)
{
return CommandHandler.Create((IConsole console) =>
{
new Win32ServiceManager().DeleteService(serviceName);
console.Out.WriteLine("The service was successfully removed.");
});
}
private sealed class Service : IWin32Service
{
private readonly Func<CancellationToken, Task> runAsync;
private CancellationTokenSource stopSource;
public Service(Func<CancellationToken, Task> runAsync, string serviceName)
{
this.runAsync = runAsync;
ServiceName = serviceName;
}
public string ServiceName { get; }
public void Start(string[] startupArguments, ServiceStoppedCallback serviceStoppedCallback)
{
stopSource = new CancellationTokenSource();
runAsync.Invoke(stopSource.Token).ContinueWith(
_ => serviceStoppedCallback.Invoke(),
TaskContinuationOptions.ExecuteSynchronously);
}
public void Stop()
{
stopSource.Cancel();
}
}
} I was going to suggest shipping something like my |
A |
Hope this is helpful. I love the project!