Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
PeterKottas committed Nov 1, 2017
2 parents 5967efc + a85dd47 commit 7814614
Show file tree
Hide file tree
Showing 8 changed files with 225 additions and 43 deletions.
5 changes: 5 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
root = true

[*.cs]
indent_style = space
indent_size = 4
15 changes: 9 additions & 6 deletions Solution/DotNetCore.WindowsService.sln
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26430.14
VisualStudioVersion = 15.0.27004.2005
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution items", "Solution items", "{8D5372CB-67BA-415A-B9B0-6C3771A6907E}"
ProjectSection(SolutionItems) = preProject
..\global.json = ..\global.json
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PeterKottas.DotNetCore.WindowsService", "..\Source\PeterKottas.DotNetCore.WindowsService\PeterKottas.DotNetCore.WindowsService.csproj", "{19F85232-0FED-439E-90BF-BDCD6567F2B3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PeterKottas.DotNetCore.WindowsService.Example", "..\Source\PeterKottas.DotNetCore.WindowsService.Example\PeterKottas.DotNetCore.WindowsService.Example.csproj", "{63C67004-73D7-4D49-8A3F-3CBC6554BB46}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PeterKottas.DotNetCore.WindowsService.MinimalTemplate", "..\Source\Templates\PeterKottas.DotNetCore.WindowsService.MinimalTemplate\PeterKottas.DotNetCore.WindowsService.MinimalTemplate.csproj", "{D5FE4BD5-85F0-42D8-89D7-C8AF272278F3}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7DFFD5B6-AE3F-478B-9893-5010AEA29096}"
ProjectSection(SolutionItems) = preProject
..\.editorconfig = ..\.editorconfig
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -36,4 +36,7 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6DD17605-730C-4A8D-8875-05FD5096DAEF}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
using PeterKottas.DotNetCore.WindowsService.Interfaces;
using Microsoft.Extensions.PlatformAbstractions;
using PeterKottas.DotNetCore.WindowsService.Interfaces;
using System;
using System.IO;
using System.Diagnostics;
using Microsoft.Extensions.PlatformAbstractions;
using System.ServiceProcess;
using System.Threading.Tasks;
using System.Timers;

namespace PeterKottas.DotNetCore.WindowsService.Example
{
public class ExampleService : IMicroService
{
private IMicroServiceController controller;

private Timer timer = new Timer(1000);

public ExampleService()
{
controller = null;
Expand All @@ -28,14 +28,23 @@ public void Start()
Console.WriteLine("I started");
Console.WriteLine(fileName);
File.AppendAllText(fileName, "Started\n");
if (controller != null)
{
controller.Stop();
}

/**
* A timer is a simple example. But this could easily
* be a port or messaging queue client
*/
timer.Elapsed += _timer_Elapsed;
timer.Start();
}

public void Stop()
private void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
File.AppendAllText(fileName, string.Format("Polling at {0}\n", DateTime.Now.ToString("o")));
}

public void Stop()
{
timer.Stop();
File.AppendAllText(fileName, "Stopped\n");
Console.WriteLine("I stopped");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
using System;
using System.IO;
using System.Diagnostics;
using Microsoft.Extensions.PlatformAbstractions;
using PeterKottas.DotNetCore.WindowsService.Base;
using PeterKottas.DotNetCore.WindowsService.Interfaces;
using Microsoft.Extensions.PlatformAbstractions;
using System;
using System.IO;

namespace PeterKottas.DotNetCore.WindowsService.Example
{
public class ExampleServiceTimer : MicroService, IMicroService
public class ExampleServiceTimer : MicroService, IMicroService
{
private IMicroServiceController controller;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
using Microsoft.Extensions.PlatformAbstractions;
using PeterKottas.DotNetCore.WindowsService;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace PeterKottas.DotNetCore.WindowsService.Example
{
public class Program
public class Program
{
public static void Main(string[] args)
{
Expand Down
135 changes: 135 additions & 0 deletions Source/PeterKottas.DotNetCore.WindowsService/ConsoleServiceHost.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
using PeterKottas.DotNetCore.WindowsService.Interfaces;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace PeterKottas.DotNetCore.WindowsService
{
/// <summary>
/// Copy of Topshelf ConsoleRunHost
/// https://github.com/Topshelf/Topshelf/blob/develop/src/Topshelf/Hosts/ConsoleRunHost.cs
/// </summary>
class ConsoleServiceHost<SERVICE>
where SERVICE : IMicroService
{
private InnerService _consoleService = null;
private HostConfiguration<SERVICE> _innerConfig = null;
private ExitCode _exitCode = 0;
private ManualResetEvent _exit = null;
private volatile bool _hasCancelled = false;

public ConsoleServiceHost(InnerService consoleService, HostConfiguration<SERVICE> innerConfig)
{
_consoleService = consoleService
?? throw new ArgumentNullException(nameof(consoleService));

_innerConfig = innerConfig
?? throw new ArgumentNullException(nameof(innerConfig));
}

internal ExitCode Run()
{
AppDomain.CurrentDomain.UnhandledException += CatchUnhandledException;

bool started = false;
try
{
Console.WriteLine("Starting up as a console service host");

_exit = new ManualResetEvent(false);
_exitCode = ExitCode.Ok;

Console.Title = _consoleService.ServiceName;
Console.CancelKeyPress += HandleCancelKeyPress;

_consoleService.Start(_innerConfig.ExtraArguments.ToArray(), () => Console.WriteLine("Stopping console service host"));
started = true;

Console.WriteLine("The {0} service is now running, press Control+C to exit.", _consoleService.ServiceName);

_exit.WaitOne();
}
catch (Exception ex)
{
Console.WriteLine("An exception occurred", ex);

return ExitCode.AbnormalExit;
}
finally
{
if (started)
StopService();

_exit.Close();
(_exit as IDisposable).Dispose();
}

return _exitCode;
}

internal void StopService()
{
try
{
if (_hasCancelled)
return;

Console.WriteLine("Stopping the {0} service", _consoleService.ServiceName);

Task stopTask = Task.Run(() => _consoleService.Stop());
if (!stopTask.Wait(TimeSpan.FromMilliseconds(150)))
throw new Exception("The service failed to stop (returned false).");

_exitCode = ExitCode.Ok;
}
catch (Exception ex)
{
Console.WriteLine("The service did not shut down gracefully: {0}", ex.ToString());
_exitCode = ExitCode.AbnormalExit;
}
finally
{
Console.WriteLine("The {0} service has stopped.", _consoleService.ServiceName);
_exitCode = ExitCode.Ok;
}
}

private void HandleCancelKeyPress(object sender, ConsoleCancelEventArgs e)
{
if (e.SpecialKey == ConsoleSpecialKey.ControlBreak)
{
Console.WriteLine("Control+Break detected, terminating service (not cleanly, use Control+C to exit cleanly)");
return;
}

e.Cancel = true;

if (_hasCancelled)
return;

Console.WriteLine("Control+C detected, attempting to stop service.");
Task stopTask = Task.Run(() => _consoleService.Stop());
if (stopTask.Wait(TimeSpan.FromMilliseconds(150)))
{
_hasCancelled = true;
_exit.Set();
}
else
{
_hasCancelled = false;
Console.WriteLine("The service is not in a state where it can be stopped.");
}
}

private void CatchUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Console.WriteLine("The service threw an unhandled exception: {0}", e.ToString());

if (!e.IsTerminating)
return;

_exitCode = ExitCode.UnhandledServiceException;
_exit.Set();
}
}
}
20 changes: 20 additions & 0 deletions Source/PeterKottas.DotNetCore.WindowsService/ExitCode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace PeterKottas.DotNetCore.WindowsService
{
/// <summary>
/// Copy of: https://github.com/Topshelf/Topshelf/blob/develop/src/Topshelf/TopshelfExitCode.cs
/// </summary>
enum ExitCode
{
Ok = 0,
AbnormalExit = 1,
SudoRequired = 2,
ServiceAlreadyInstalled = 3,
ServiceNotInstalled = 4,
StartServiceFailed = 5,
StopServiceFailed = 6,
ServiceAlreadyRunning = 7,
UnhandledServiceException = 8,
ServiceNotRunning = 9,
SendCommandFailed = 10,
}
}
49 changes: 32 additions & 17 deletions Source/PeterKottas.DotNetCore.WindowsService/ServiceRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,22 +128,38 @@ public static int Run(Action<HostConfigurator<SERVICE>> runAction)
try
{
runAction(hostConfiguration);
if (innerConfig.Action == ActionEnum.Run || innerConfig.Action == ActionEnum.RunInteractive)
{
var controller = new MicroServiceController(
() =>
{
var task = Task.Factory.StartNew(() =>
{
UsingServiceController(innerConfig, (sc, cfg) => StopService(cfg, sc));
});
//task.Wait();
}
);
innerConfig.Service = innerConfig.ServiceFactory(innerConfig.ExtraArguments, controller);
}
ConfigureService(innerConfig);
return 0;
if (innerConfig.Action == ActionEnum.Run)
innerConfig.Service = innerConfig.ServiceFactory(innerConfig.ExtraArguments,
new MicroServiceController(() =>
{
var task = Task.Factory.StartNew(() =>
{
UsingServiceController(innerConfig, (sc, cfg) => StopService(cfg, sc));
});
}
));
else if (innerConfig.Action == ActionEnum.RunInteractive)
{
var consoleService = new InnerService(innerConfig.Name, () => Start(innerConfig), () => Stop(innerConfig));
var consoleHost = new ConsoleServiceHost<SERVICE>(consoleService, innerConfig);

innerConfig.Service = innerConfig.ServiceFactory(innerConfig.ExtraArguments,
new MicroServiceController(() =>
{
var task = Task.Factory.StartNew(() =>
{
consoleHost.StopService();
});
}
));

// Return the console host run result, so we get some idea what failed if result is not OK
return (int)consoleHost.Run();
}

ConfigureService(innerConfig);

return 0;
}
catch (Exception e)
{
Expand Down Expand Up @@ -304,7 +320,6 @@ private static void ConfigureService(HostConfiguration<SERVICE> config)
serviceHost.Run();
break;
case ActionEnum.RunInteractive:
Start(config);
break;
case ActionEnum.Stop:
UsingServiceController(config, (sc, cfg) => StopService(cfg, sc));
Expand Down

0 comments on commit 7814614

Please sign in to comment.