Getting notified by a Windows Service status change in C# .NET

The ManagementEventWatcher object in the System.Management namespace makes it possible to subscribe to events within the WMI – Windows Management Instrumentation – context. A change in the status of a Windows service is such an event and it’s possible to get notified when that happens.

We saw examples of WMI queries on this blog before – check the link below – and the ManagementEventWatcher object also requires an SQL-like query string. Consider the following function:

private static void RunManagementEventWatcherForWindowsServices()
{
	EventQuery eventQuery = new EventQuery();
	eventQuery.QueryString = "SELECT * FROM __InstanceModificationEvent within 2 WHERE targetinstance isa 'Win32_Service'";	
	ManagementEventWatcher demoWatcher = new ManagementEventWatcher(eventQuery);
	demoWatcher.Options.Timeout = new TimeSpan(1, 0, 0);
	Console.WriteLine("Perform the appropriate change in a Windows service according to your query");
	ManagementBaseObject nextEvent = demoWatcher.WaitForNextEvent();			
	ManagementBaseObject targetInstance = ((ManagementBaseObject)nextEvent["targetinstance"]);
	PropertyDataCollection props = targetInstance.Properties;
	foreach (PropertyData prop in props)
	{
		Console.WriteLine("Property name: {0}, property value: {1}", prop.Name, prop.Value);
	}

	demoWatcher.Stop();
}

We declare the query within an EventQuery object. Windows services are of type “Win32_Service” hence the “where targetinstance isa ‘Win32_Service'” clause. “within 2” means that we want to be notified 2 seconds after the status change has been detected. A change event is represented by the __InstanceModificationEvent class. There are many similar WMI system classes. A creation event corresponds to the __InstanceCreationEvent class. So the query is simply saying that we want to know of any status change in any Windows service 2 seconds after the change.

The timeout option means that the ManagementEventWatcher object will wait for the specified amount of time for the event to occur. After this a timeout exception will be thrown so you’ll need to handle that.

In order to read the properties of the Windows service we need to go a level down to “targetinstance” and read the properties of that ManagementBaseObject. Otherwise the “nextEvent” object properties are not too informative.

Run this code, open the Windows services window and stop or pause any Windows service. I stopped the Tomcat7 service running on my PC and got the following Console output:

Stopping any service caught by event watcher

You can of course refine your query using the property names of the target instance. You can always check the property names on MSDN. E.g. if you open the above link to the Win32_Service object then you’ll see that it has a “state” and a “name” property. So in case you’ll want to know that a service name “Tomcat7” was stopped then you can have the following query:

eventQuery.QueryString = "SELECT * FROM __InstanceModificationEvent within 2 WHERE targetinstance isa 'Win32_Service' and targetinstance.state = 'Stopped' and targetinstance.name = 'Tomcat7'";

In this case starting Tomcat7 won’t trigger the watcher. Neither will stopping any other Windows service. The event watcher will only react if a service names “Tomcat7” was stopped, i.e. the “Status” property of the target instance was set to “Stopped”.

You can view all posts related to Diagnostics here.

Advertisements

Getting notified by a Windows Service status change in C# .NET

The ManagementEventWatcher object in the System.Management namespace makes it possible to subscribe to events within the WMI – Windows Management Instrumentation – context. A change in the status of a Windows service is such an event and it’s possible to get notified when that happens.

We saw examples of WMI queries on this blog before – check the link below – and the ManagementEventWatcher object also requires an SQL-like query string. Consider the following function:

private static void RunManagementEventWatcherForWindowsServices()
{
	EventQuery eventQuery = new EventQuery();
	eventQuery.QueryString = "SELECT * FROM __InstanceModificationEvent within 2 WHERE targetinstance isa 'Win32_Service'";	
	ManagementEventWatcher demoWatcher = new ManagementEventWatcher(eventQuery);
	demoWatcher.Options.Timeout = new TimeSpan(1, 0, 0);
	Console.WriteLine("Perform the appropriate change in a Windows service according to your query");
	ManagementBaseObject nextEvent = demoWatcher.WaitForNextEvent();			
	ManagementBaseObject targetInstance = ((ManagementBaseObject)nextEvent["targetinstance"]);
	PropertyDataCollection props = targetInstance.Properties;
	foreach (PropertyData prop in props)
	{
		Console.WriteLine("Property name: {0}, property value: {1}", prop.Name, prop.Value);
	}

	demoWatcher.Stop();
}

We declare the query within an EventQuery object. Windows services are of type “Win32_Service” hence the “where targetinstance isa ‘Win32_Service'” clause. “within 2” means that we want to be notified 2 seconds after the status change has been detected. A change event is represented by the __InstanceModificationEvent class. There are many similar WMI system classes. A creation event corresponds to the __InstanceCreationEvent class. So the query is simply saying that we want to know of any status change in any Windows service 2 seconds after the change.

The timeout option means that the ManagementEventWatcher object will wait for the specified amount of time for the event to occur. After this a timeout exception will be thrown so you’ll need to handle that.

In order to read the properties of the Windows service we need to go a level down to “targetinstance” and read the properties of that ManagementBaseObject. Otherwise the “nextEvent” object properties are not too informative.

Run this code, open the Windows services window and stop or pause any Windows service. I stopped the Tomcat7 service running on my PC and got the following Console output:

Stopping any service caught by event watcher

You can of course refine your query using the property names of the target instance. You can always check the property names on MSDN. E.g. if you open the above link to the Win32_Service object then you’ll see that it has a “state” and a “name” property. So in case you’ll want to know that a service name “Tomcat7” was stopped then you can have the following query:

eventQuery.QueryString = "SELECT * FROM __InstanceModificationEvent within 2 WHERE targetinstance isa 'Win32_Service' and targetinstance.state = 'Stopped' and targetinstance.name = 'Tomcat7'";

In this case starting Tomcat7 won’t trigger the watcher. Neither will stopping any other Windows service. The event watcher will only react if a service names “Tomcat7” was stopped, i.e. the “Status” property of the target instance was set to “Stopped”.

You can view all posts related to Diagnostics here.

Using a Windows service in your .NET project part 8: Windows Service body part 2

Introduction

In the previous post of this series we started adding some code to the Windows service. We also implemented a simplified logging system to be able to track what’s happening within the service. In this post we’ll start adding code to actually run the HTTP jobs inserted in MongoDb.

So open the demo application in VS and let’s start.

HttpJobRunner process

We established before that HttpJobRunner will need an IHttpJobService to retrieve the new jobs and an IHttpJobExecutionService to run them. Open HttpJobRunner.cs and add the following private fields:

private readonly IHttpJobService _httpJobService;
private readonly IHttpJobExecutionService _httpJobExecutionService;

Extend the constructor as follows:

public HttpJobRunner(ILoggingService loggingService, IHttpJobService httpJobService, IHttpJobExecutionService httpJoExecutionService)
{
	InitializeComponent();
	if (loggingService == null) throw new ArgumentNullException("LoggingService");
	if (httpJobService == null) throw new ArgumentNullException("HttpJobService");
	if (httpJobExecutionService == null) throw new ArgumentNullException("HttpJobExecutionService");
	_loggingService = loggingService;
	_httpJobService = httpJobService;
	_httpJobExecutionService = httpJobExecutionService;
	TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
}

Program.cs in Demo.HttpJobRunner will start complaining of course. Open that file and extend its body to the following:

static class Program
{
	static void Main()
	{
		ServiceBase[] ServicesToRun;
		IHttpJobService httpJobService = BuildHttpJobService();
		IHttpJobExecutionService httpJobExecutionService = BuildHttpJobExecutionService(httpJobService);
		ServicesToRun = new ServiceBase[] 
                { 
				
                   new HttpJobRunner(new FileBasedLoggingService(@"c:\logging\log.txt"), httpJobService, httpJobExecutionService)
                };
		ServiceBase.Run(ServicesToRun);
	}

	private static IHttpJobService BuildHttpJobService()
	{
		IConfigurationRepository configurationRepository = new ConfigFileConfigurationRepository();
		IDatabaseConnectionSettingsService dbConnectionSettingsService = new HttpJobDatabaseConnectionService(configurationRepository);
		IJobRepository jobRepository = new JobRepository(dbConnectionSettingsService);
		IHttpJobService httpJobService = new HttpJobService(jobRepository);
		return httpJobService;
	}

	private static IHttpJobExecutionService BuildHttpJobExecutionService(IHttpJobService httpJobService)
	{
		IHttpJobExecutionService httpJobExecutionService = new HttpJobExecutionService(httpJobService, new HttpJobUrlService(new HttpClientService()));
		return httpJobExecutionService;
	}
}

You’ll need to add a reference to all other projects in the solution from Demo.HttpJobRunner for this code to compile. We do nothing else but declare the concrete implementations of the abstractions in the entry point of the Windows service application.

HttpJobRunner has all the dependencies it needs now so we can continue. Well, almost. recall that we saved the connection string and the HttpJobs table name in app.config of the Console application. Check the files available for the Windows service project. There should be another app.config. When we deploy the Windows service then it will consult its own app.config, which is totally independent of the app.config of ConsoleConsumer. It won’t magically have access to the configuration file of the Console app. This means that we need to add the same settings to HttpJobRunner.app.config too. Copy the settings so that the Windows service app.config looks as follows:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <add name="HttpJobsConnectionString" connectionString="mongodb://localhost"/>
  </connectionStrings>
  <appSettings>
    <add key="HttpJobsDatabaseName" value="HttpJobsDatabase"/>
  </appSettings>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
</configuration>

The plan for the job execution process in the service is the following:

  • Check the database for new jobs periodically
  • If new jobs are found then log them and start running them on a different thread
  • Log the result of the job

It’s important to start a new thread for each new job otherwise the job process will block all other code, including checking the database for new jobs. This way the service will be able to execute multiple jobs simultaneously.

We can instruct the Windows service to carry out a certain action using the Timer object in the System.Timers namespace. Add the following private field to the field list of HttpJobRunner:

private readonly Timer _jobCollectionTimer;

…and add the following code into the HttpJobRunner constructor to initialise the timer and attach an event listener to the Timer.Elapsed event:

_jobCollectionTimer = new Timer(10000);
_jobCollectionTimer.Elapsed += _jobCollectionTimer_Elapsed;

…where _jobCollectionTimer_Elapsed was generated by Visual Studio and looks like this by default:

void _jobCollectionTimer_Elapsed(object sender, ElapsedEventArgs e)
{
	throw new NotImplementedException();
}

This handler is called every 10 seconds, i.e. 10000 milliseconds we specified in the Timer’s constructor. Remove the “throw new…” bit from the handler body and add the “async” keyword in front of “void”:

async void _jobCollectionTimer_Elapsed(object sender, ElapsedEventArgs e)

We’ll have an awaitable asynchronous method call within the handler body hence the need for the async keyword.

We need to start the Timer as well. Add the following code…

_jobCollectionTimer.Start();

…to 3 overridden Windows service events:

  • OnStart
  • OnContinue
  • OnPause

Here’s the body of the _jobCollectionTimer_Elapsed handler:

async void _jobCollectionTimer_Elapsed(object sender, ElapsedEventArgs e)
{
	try
	{
		GetHttpJobsResponse getAllNewHttpJobsResponse = _httpJobService.GetNewHttpJobs();
		if (getAllNewHttpJobsResponse.OperationException != null) throw getAllNewHttpJobsResponse.OperationException;
		IEnumerable<HttpJob> newJobs = getAllNewHttpJobsResponse.HttpJobs;
		if (newJobs.Count() > 0)
		{
			LogNewJobs(newJobs);
			foreach (HttpJob httpJob in newJobs)
			{
				await Task.Factory.StartNew(async () => await RunSingleHttpJob(httpJob));
			}
		}
	}
	catch (Exception ex)
	{
		LogException(ex);
	}
}

…and here come the private helper methods:

private async Task RunSingleHttpJob(HttpJob httpJob)
{
	LogStartOfNewJob(httpJob);
	try
	{
		await _httpJobExecutionService.Execute(httpJob);
	}
	catch (Exception ex)
	{
		LogException(ex);
	}
	LogEndOfNewJob(httpJob);
}

private void LogStartOfNewJob(HttpJob newJob)
{
	StringBuilder sb = new StringBuilder();
	sb.Append("About to start job ").Append(newJob.CorrelationId);
	_loggingService.LogInfo(this, sb.ToString());
}

private void LogEndOfNewJob(HttpJob newJob)
{
	StringBuilder sb = new StringBuilder();
	sb.Append("Finished running job ").Append(newJob.CorrelationId);
	_loggingService.LogInfo(this, sb.ToString());
}

private void LogNewJobs(IEnumerable<HttpJob> newJobs)
{
	StringBuilder sb = new StringBuilder();
	sb.Append("Found the following new jobs: ");
	foreach (HttpJob httpJob in newJobs)
	{
		sb.Append(httpJob.CorrelationId).Append(", ");
	}
	_loggingService.LogInfo(this, sb.ToString());
}

private void LogException(Exception exception)
{
	StringBuilder sb = new StringBuilder();
	sb.Append("Exception caught in HttpJobRunner:")
		.Append(NL).Append("Exception message: ").Append(exception.Message)
		.Append(NL).Append("Exception stacktrace: ").Append(exception.StackTrace);
	_loggingService.LogError(this, sb.ToString(), exception);
}

As outlined in the expected work flow the HttpJobRunner will first extract the list of new jobs. If there are any new jobs then they are logged. Then each new job is started on its own thread. Within RunSingleHttpJob we first log the start of the job, instruct the execution service to execute the job and finally log the end of the process.

This should be all we need to have a simple but functioning system. Rebuild the solution, uninstall the service and install it again as we did before. Check the log file to make sure the service has “checked in”. Then run the Console app, enter a couple of URLs and wait for the status messages. In my case I got the following:

Service starting executing job

The same message appeared a couple of times, but then…

Job execution ongoing

Job execution near finish

…and finally…:

Job execution finished

So the job was successfully executed by the HttpJobRunner service. Let’s check the log:

22/09/2014 20:14:58: source: Demo.WindowsService.HttpJobRunner, level: INFO, message: Found the following new jobs: 9e9f332e-147a-485a-8904-5360b30751b5, , any exception: None
22/09/2014 20:14:58: source: Demo.WindowsService.HttpJobRunner, level: INFO, message: About to start job 9e9f332e-147a-485a-8904-5360b30751b5, any exception: None
22/09/2014 20:15:04: source: Demo.WindowsService.HttpJobRunner, level: INFO, message: Finished running job 9e9f332e-147a-485a-8904-5360b30751b5, any exception: None

…which looks fine too.

We’re done, this concludes the series on a possible use of a Windows service in a .NET project. Windows services can be used for other things such as hosting another service, such as WCF, but I wanted to concentrate on their function to execute periodic and/or long running processes.

View the list of posts on Architecture and Patterns here.

Using a Windows service in your .NET project part 7: Windows Service body part 1

Introduction

In the previous post we saw how to install and uninstall the Windows service of our demo project. Currently the service doesn’t do anything as there’s no method implementation anywhere but it’s good that we can safely install and uninstall it.

Let’s review what this service should be able to do and what dependencies it will need:

  • It should periodically check the MongoDb database for new Http jobs. We have a service method for this in IHttpJobService
  • It should then execute those jobs using an IHttpJobExecutionService
  • Log the actions using an interface and implementation we haven’t seen yet

I need to stress the importance of logging in a Windows service. First of all it’s vital to log at least some activity in any real business application so that you can trace what your users are doing and find the source of exceptions. It’s even more important to have logging in place for an application with no user interface such as a Windows service. A Windows service cannot even send messages to a command prompt for you to see during testing. If you have no logging in place then you’ll be wondering what is going on if the service doesn’t perform its job.

We won’t build a full-blown logging system here, just a simplified file-based one. You can read more about logging in two blog posts: an introduction and a concrete example with log4net.

Logging

Let’s get this done first. We’ll reuse the logging interface from the logging intro referred to above. Open the Demo.Infrastructure project and add a new folder called Logging. Insert the following interface:

public interface ILoggingService
{
	void LogInfo(object logSource, string message, Exception exception = null);
	void LogWarning(object logSource, string message, Exception exception = null);
	void LogError(object logSource, string message, Exception exception = null);
	void LogFatal(object logSource, string message, Exception exception = null);
}

We’ll implement a home-made file-based logging service. Insert the following class to the Logging folder:

public class FileBasedLoggingService : ILoggingService
{
	private readonly string _logFileFullPath;

	public FileBasedLoggingService(string logFileFullPath)
	{
		if (string.IsNullOrEmpty(logFileFullPath)) throw new ArgumentException("Log file full path");
		FileInfo logFileInfo = new FileInfo(logFileFullPath);
		if (!logFileInfo.Exists) throw new ArgumentNullException("Log file does not exist");
		_logFileFullPath = logFileFullPath;
	}

	public void LogInfo(object logSource, string message, Exception exception = null)
	{
		AppendMessage(logSource, message, "INFO", exception);
	}

	public void LogWarning(object logSource, string message, Exception exception = null)
	{
		AppendMessage(logSource, message, "WARNING", exception);
	}

	public void LogError(object logSource, string message, Exception exception = null)
	{
		AppendMessage(logSource, message, "ERROR", exception);
	}

	public void LogFatal(object logSource, string message, Exception exception = null)
	{
		AppendMessage(logSource, message, "FATAL", exception);
	}

	private void AppendMessage(object source, string message, string level, Exception exception)
	{
		try
		{
			File.AppendAllText(_logFileFullPath, FormatMessage(source, message, level, exception));
		}
		catch { }
	}

	private string FormatMessage(object source, string message, string level, Exception exception)
	{
		return string.Concat(Environment.NewLine, DateTime.UtcNow.ToString(), ": source: ", source.ToString(), ", level: ", level, ", message: ", message, ", any exception: ", (exception == null ? "None" : exception.Message) );
	}
}

In a real application you’d use a professional tool to log to a file but that would unnecessarily sidetrack us now. Also, we could take the level of abstraction even further. Note that FileBasedLoggingService directly uses objects like File and FileInfo which make it difficult to test the methods in this class. A more complete solution would require us to separate out the concrete file-based operations to an abstraction. We’ll leave the implementation as it is for now. You can clean up this code if you’d like to as a homework. You can take a look at this post where I go through how to factor out file-related operations. It ties in well with what I’ve written in this paragraph.

Create a text file like “log.txt” somewhere on your hard drive and take note of its full path. Make sure that the file is editable by the service. For demo purposes you can give full access to Everyone so that we don’t need to trace any logging-related exceptions:

Set log file access to Everyone

Note how we simply ignore any exceptions thrown within AppendMessage. This is called fire-and-forget. This strategy can be acceptable in logging as we don’t want the application to stop just because e.g. the log file is inaccessible. In a testing phase you can catch the exception and log it somewhere else, such as the event log, in case you don’t see the logging messages in the target file, here’s an example:

private void AppendMessage(object source, string message, string level, Exception exception)
{
	try
	{
		File.AppendAllText(_logFileFullPath, FormatMessage(source, message, level, exception));
	}
	catch (Exception ex)
	{
		string s = "HttpJobRunner";
		string log = "Application";
		if (!EventLog.SourceExists(s))
		{
			EventLog.CreateEventSource(s, log);
		}
		EventLog.WriteEntry(s, "Exception in HttpJobRunner logging: " + ex.Message, EventLogEntryType.Error, 1234);
	}
}

Let’s add this as a dependency to HttpJobRunner. At present HttpJobRunner- the partial class that inherits from ServiceBase – is quite empty with a constructor that calls upon InitializeComponent and two empty overridden methods.

Change the implementation as follows:

public partial class HttpJobRunner : ServiceBase
{
	private readonly ILoggingService _loggingService;

	public HttpJobRunner(ILoggingService loggingService)
	{
		InitializeComponent();
		if (loggingService == null) throw new ArgumentNullException("LoggingService");
		_loggingService = loggingService;
	}

	protected override void OnStart(string[] args)
	{
		_loggingService.LogInfo(this, "HttpJobRunner starting up");
	}

	protected override void OnStop()
	{
		_loggingService.LogInfo(this, "HttpJobRunner stopping");
	}
}

You’ll need to add a project reference to the Infrastructure layer for this to compile. We let an ILoggingService be injected to HttpJobRunner through its constructor. Then we simply take note of the Windows service starting up and shutting down in the log file. Build the project and you should get an exception from Program.cs in the Windows service layer: HttpJobRunner doesn’t have an empty constructor. Change the implementation as follows:

static void Main()
{
	ServiceBase[] ServicesToRun;
	ServicesToRun = new ServiceBase[] 
        { 
             new HttpJobRunner(new FileBasedLoggingService(@"c:\logging\log.txt"))
       };
	ServiceBase.Run(ServicesToRun);
}

Make sure you insert the full path of the log file that you created before in the FileBasedLoggingService constructor.

Let’s see if this works. Rebuild the project and re-install the Windows service following the steps outlined in the previous part. Check the contents of the log file, there should be one entry similar to the following:

20/09/2014 20:07:25: source: Demo.WindowsService.HttpJobRunner, level: INFO, message: HttpJobRunner starting up, any exception: None

You can stop and/or restart the service in the Services window:

Stop and restart a service

The log messages should be added to the log file accordingly, e.g.:

20/09/2014 21:00:42: source: Demo.WindowsService.HttpJobRunner, level: INFO, message: HttpJobRunner starting up, any exception: None
20/09/2014 21:01:02: source: Demo.WindowsService.HttpJobRunner, level: INFO, message: HttpJobRunner shutting down, any exception: None
20/09/2014 21:01:03: source: Demo.WindowsService.HttpJobRunner, level: INFO, message: HttpJobRunner starting up, any exception: None

Great, we have some logging in place.

Restarting a Windows service can have negative consequences in your application. If the service is carrying out a job without saving its current state somewhere then restarting the service will in effect kill the job. So at a minimum we want to know any time the service status has changed. Add the following overrides to HttpJobRunner:

protected override void OnShutdown()
{
	_loggingService.LogInfo(this, "HttpJobRunner shutting down");
}

protected override void OnContinue()
{
	_loggingService.LogInfo(this, "HttpJobRunner continuing");
}

protected override void OnPause()
{
	_loggingService.LogInfo(this, "HttpJobRunner pausing");
}	

This doesn’t of course solve the problem of interrupted jobs but at least we know that something noteworthy has happened which can be the cause of a failed job. It is your responsibility to make your service “clever” enough to pick up an interrupted job when it’s restarted. In our example the service could check all jobs that have been started but not yet finished and start from there – or simply restart the job from the beginning. However, that’s beyond the scope of this series, but it’s still good to know about this issue.

Before we finish this post let’s add one more method to HttpJobRunner. The goal is that the service is never restarted due to an unhandled exception. If there’s an uncaught exception somewhere during the code execution the the service will fail and act according to the failure policy, such as “Restart the service” in our case. However, as noted above service restarts can be detrimental. It’s not always possible to catch all exceptions in a large code base even if you put try-catch clauses everywhere.

There’s a solution to catch all uncaught exceptions in a Windows service though. I mentioned it already in this short post on threading. We’ll reuse much of it here.

Within the HttpJobRunner constructore start typing…

TaskScheduler.UnobservedTaskException +=

…and press Tab twice. This will add a default handler that’s called whenever an uncaught exception bubbles up:

void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
{
	throw new NotImplementedException();
}

Insert the following implementation:

void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
{
	e.SetObserved();
	AggregateException aggregateException = (e.Exception as AggregateException).Flatten();
	aggregateException.Handle(ex =>
	{				
		return true;
	});
	List<Exception> inners = aggregateException.InnerExceptions.ToList();
	StringBuilder sb = new StringBuilder();
	sb.Append("Unhandled exception caught in HttpJobRunner by the generic catch-all handler.")
				.Append(NL);
	if (inners != null && inners.Count > 0)
	{
		foreach (Exception inner in inners)
		{
			sb.Append("Exception: ").Append(inner.Message).Append(NL).Append("Stacktrace: ").Append(NL)
				.Append(inner.StackTrace).Append(NL);
			if (inner.InnerException != null)
			{
				Exception innerInner = inner.InnerException;
				sb.Append("Inner exception has also an inner exception. Message: ")
					.Append(innerInner.Message).Append(". ").Append(NL)
					.Append("Stacktrace: ").Append(innerInner.StackTrace);
							
			}
			sb.Append(NL).Append(NL);
		}
	}
	else
	{
		sb.Append("No inner exceptions.").Append(NL);
		sb.Append("Plain message: ").Append(aggregateException.Message).Append(NL);
		sb.Append("Stacktrace: ").Append(aggregateException.StackTrace).Append(NL);
	}

	_loggingService.LogFatal(this, sb.ToString());
}

…where “NL” refers to a private variable to shorten Environment.NewLine:

private readonly string NL = Environment.NewLine;

This is enough for now. We’ll continue with the service implementation in the next post.

View the list of posts on Architecture and Patterns here.

Using a Windows service in your .NET project part 6: Windows Service installation

Introduction

In the previous post we went through the basics of a Windows Service. We saw how to set its properties in code. In this post we’ll see how the service can be installed.

Installation

There are at least three ways to install a Windows Service:

  • Manually calling the InstallUtil tool which is usually located at C:\Windows\Microsoft.NET\Framework64\v4.0.30319 or C:\Windows\Microsoft.NET\Framework\v4.0.30319
  • Call upon InstallUtil in a .bat file
  • Use the InstallShield limited edition project type in Visual Studio and add the components of the installation package

The first option is a bit cumbersome I think. You have to type in the uninstall and install commands to InstallUtil in a command prompt and provide the necessary command parameters. The third option, as far as I can remember, is not by default suitable for installing Windows services. The InstallShield wizard is meant to install “normal” desktop applications, like games. The last time I used the InstallShield project type I had to add a plugin from WiX in order to make it work with Windows services and it wasn’t fun.

We’ll instead go with the second option. There’s a definitive advantage with option 2 compared to the other ones. In a more advanced “enterprise” scenario you’ll likely store your code in some repository like SVN or GitHub. Then the code will be exported to a Continuous Deployment or Continuous Integration tool such as Jenkins or TeamCity. These tools can run the installation script to automatically install the Windows service as part of the automated deployment process.

The installation scripts

The scripts are very simple. We’ll have one for uninstalling a service and one for installing it. Some of the process will be manual but it can be automated with batch files in an automatic deployment scenario. When we install the Windows service we have to make sure that any existing service of the same name is uninstalled otherwise we’ll get an exception hence the need for an uninstaller.

I’m not aware of a batch file item type in Visual Studio so open Notepad. Add the following content to it:

@ECHO OFF

echo Installing HttpJobRunner service…
echo ———————————————–
C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil /i “C:\Program Files (x86)\WindowsServiceDemo\Demo.WindowsService.exe”
C:\Windows\System32\sc.exe failure “HttpJobRunner” reset= 0 actions= restart/1000
echo ———————————————–
echo Done.
pause

We call InstallUtil, instruct it to install a service with the ‘i’ flag. The install command refers to a folder we haven’t set up yet but will do it soon. Then, as hinted at in the previous post we’ll add an extra call to set the failure mode to restart the service. Be exact with the arguments, including the whitespaces: “reset= 0” and not “reset = 0” or “reset =0”. In my experience putting the space in the wrong position will result in a failure. We then call “pause” to leave the command prompt open so that we can read the outcome. You can later remove that command so that the window closes itself automatically.

We’ll save the file in the same directory where Demo.HttpJobRunner resides. If you’re not sure where it is then right click the Windows service project in the solution explorer and click Open Folder in File Explorer. That is the folder where we’ll save the file we wrote in Notepad. Save the file as “_installer.bat”.

The file first won’t be visible in Solution explorer. Click the “Show all files” and “Refresh” buttons, it should appear:

Installer file shown in Visual Studio

Right-click the file and select “Include in project”. Locate the properties window to set the “Copy to Output Directory” option to Copy always:

Select Copy Always option for installer files

We do this so that the _installer.bat file is also copied to the deploy folder.

Let’s create another file in Notepad with the following content:

@ECHO OFF

echo Uninstalling HttpJobRunner service…
echo ———————————————–
C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil /u “C:\Program Files (x86)\WindowsServiceDemo\Demo.WindowsService.exe”
echo ———————————————–
echo Done.
pause

As expected this will uninstall the service. Save the file as “_uninstall.bat” and add it to the project the same way as we did with _installer.bat. At the end you should have both files listed in Visual Studio:

All installer files present in Visual Studio solution explorer

Next let’s set up the deployment folder. Normally all applications are installed in the Program Files or Program Files (x86) folder. Let’s follow that convention. Create a folder called WindowsServiceDemo within Program Files (x86) or Program Files. Within that new folder add another folder called Deploy. In Visual Studio set the Build path to this Deploy folder for Demo.HttpJobRunner:

Set build output to new installation folder

Note that the _installer.bat and _uninstaller.bat files refer to Program Files (x86) but if you opted for Program Files then make sure to update the installer files accordingly.

We should be good to go. Right-click the Demo.HttpJobRunner project and select Rebuild. The Deploy folder should be populated:

First build package loaded to new installation path

Copy all files except for _installer.bat and _uninstaller.bat one level up, i.e. under “WindowsServiceDemo”. Then return to Deploy and run _installer.bat as an Administrator. View the notices on the Command prompt. It might tell you that the OnCommitted event has failed but the exception message is misleading I think: you haven’t specified a process file name or something similar. I’ve seen it before, I’m not 100% sure why it happens but I’m pretty certain that it’s because the Process object has not enough rights to run the “sc” command. It’s OK, the installer script will take care of that. In case you don’t see any exceptions from the OnCommitted phase in the output then you can remove the extra call to “sc” in the _installer.bat file. Otherwise just keep it and remove the SetRecoveryOptions method from HttpJobRunnerInstaller if you wish.

OK, let’s see if the service is up and running. It should figure in the list of services and should be running:

HttpJobRunner installed as Windows service

Check its properties. They should be set according to what we provided in code. Check especially the failure mode:

Failure mode for HttoJobRunner

Let’s now test the uninstaller. Go back to the Deploy folder and run _uninstaller.bat as an administrator. After some seconds and output to the console the uninstall process should succeed. Refresh the services list and HttpJobRunner should be gone:

HttpJobRunner uninstalled as Windows service

The service doesn’t do anything yet when installed because we left HttpJobRunner.cs untouched.

The installation process at present is a bit manual but can be automated with new batch files. Assuming that the service is up and running these are the steps to install a new service:

  • Build the project in Visual Studio – this will put the HttpJobRunner deploy package to the Deploy folder including _installer and _uninstaller.bat
  • Run _uninstaller.bat
  • Copy the deploy files to the WindowsServiceDemo folder except for the installation files
  • Run _installer.bat

In the next post we’ll start adding some action to the Windows Service.

View the list of posts on Architecture and Patterns here.

Using a Windows service in your .NET project part 5: the Windows Service project basics

Introduction

In the previous post of this series we added the console UI to the application. The user can now add a HttpJob to the MongoDb database but there’s no job handler yet. In this post we’ll start building the Windows service which will do just that.

I’ll assume you have no or very limited knowledge of Windows services so I’ll go through this at quite a slow pace.

We’ll be working on the demo project we’ve been building so far so have it ready in Visual Studio 2012/2013.

The Windows Service layer

There’s a special project type in Visual Studio professional called… …Windows Service! It’s found under the Windows category VS 2012/2013 Pro:

Windows Service project type in Visual Studio

Call it Demo.HttpJobRunner and add it to the solution. The template will add a couple of elements to the project:

Windows Service as it is first set up by Visual Studio

You’ll recognise App.config probably. As the Windows Service will be a standalone application it must have its own configuration file with its own settings, connection strings etc. It won’t be able to read the app.config file of Demo.ConsoleConsumer of course.

Program.cs seems probably familiar from standard console apps where Program.cs includes the entry point to the application, i.e. Main. This is no different as there we have a Main method which calls upon an array of services to be started. You can add other services to the ServicesToRun method but we’ll have only one. The Main in Program.cs will at present start the service called Service1. That refers to Service1.cs of course. That’s the default name of the file, similar to “Class1” in C# library projects. You’ll see that there are 2 elements called Service1. The top element is the design view which includes the code behind file Service1.cs. Right-click the design file, select Rename, and rename it to HttpJobRunner. You’ll get a question asking if you want to rename the class to HttpJobRunner, click Yes. This is what you should have after the renaming:

Service file name after renaming

So now we have 3 elements called HttpJobRunner. The two “normal” C# classes are partial so they are joint files really. One of the HttpJobRunner.cs files will have the following class declaration:

partial class HttpJobRunner

This small file contains code required to properly run the service, we won’t change anything in there.

The other part of HttpJobRunner has a different declaration:

public partial class HttpJobRunner : ServiceBase

So it derives from ServiceBase in the System.ServiceProcess namespace. The class includes the overridden OnStart and OnStop methods which are empty by default:

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

	protected override void OnStart(string[] args)
	{
	}

	protected override void OnStop()
	{
	}
}

These methods are triggered when the service is started and stopped. We’ll return to these methods in the next post.

Service installers

The service will need to be installed. Add a new item of type Installer class to the service project:

Add installer class to Windows Service

The template will add the following elements:

Initial view of Windows Service installer

We’ll change the properties of our Windows service within the installer. There are two installer-type objects that we’ll use:

  • ServiceInstaller: to define the service description, display name, service name, start type and some other properties like service dependencies
  • ServiceProcessInstaller: the define under which account the service should be running

Windows Service properties

Navigate to the partial HttpJobRunnerInstaller class which includes the InitializeComponent method:

Initialise component method in windows service installer

Under…

private System.ComponentModel.IContainer components = null;

…add the following installer classes and a service name field:

private ServiceInstaller _httpJobServiceInstaller;
private ServiceProcessInstaller _httpJobServiceProcessInstaller;
private String _serviceName = "HttpJobRunner";

In InitializeComponent() we already have one line of code that constructs a new Container() object. We’ll leave it there. Below that let’s construct the installer objects:

_httpJobServiceInstaller = new ServiceInstaller();
_httpJobServiceProcessInstaller = new ServiceProcessInstaller();

Next we’ll set up the service properties:

_httpJobServiceInstaller.Description = "HttpJob runner service which carries out a series of HTTP calls";
_httpJobServiceInstaller.DisplayName = "HttpJob automatic runner";
_httpJobServiceInstaller.ServiceName = _serviceName;
_httpJobServiceInstaller.StartType = ServiceStartMode.Automatic;

The service name is not the same as the display name. The service name is how Windows can find the service.

Example:

Windows service anatomy in the services window

Right-click a service and select Properties. “Name” corresponds to DisplayName.

We have the following startup types:

  • Automatic: start the service as soon as the computer starts. The user doesn’t even need to log in
  • Manual: the user must manually start the service
  • Disabled: the service won’t start until the user changes the startup type and starts the service manually

You’ll notice a tab called Dependencies in the Properties window. It lists any other Windows service service that this particular service depends on. If those services are not available then the dependent service won’t run either. You can add the dependencies by name using the ServicesDependedOn property. Example:

_httpJobServiceInstaller.ServicesDependedOn = new string[] { "servicename1", "servicename2" };

A Windows Service can run under a specific security context:

  • LocalService: runs as a non-privileged user on the local computer with anonymous credentials
  • NetworkService: enables the service to authenticate to another computer on the network
  • LocalSystem: almost unlimited privileges where the service presents the computer’s credentials to any remote server
  • User: runs under the security settings of a user

The following will specify the security context using the ServiceAccount enumeration:

_httpJobServiceProcessInstaller.Account = ServiceAccount.LocalSystem;

In case you want to run the service under a specific user then you can add the username and password details here:

_httpJobServiceProcessInstaller.Account = ServiceAccount.User;
_httpJobServiceProcessInstaller.Username = "username";
_httpJobServiceProcessInstaller.Password = "password";

Finally we add our installers to the list of installers that need to run:

Installers.AddRange(new System.Configuration.Install.Installer[]
	{
		_httpJobServiceInstaller
		, _httpJobServiceProcessInstaller
	});

So to recap we have the following code in InitializeComponent right now:

private void InitializeComponent()
{
	components = new System.ComponentModel.Container();
	_httpJobServiceInstaller = new ServiceInstaller();
	_httpJobServiceProcessInstaller = new ServiceProcessInstaller();
	_httpJobServiceInstaller.Description = "HttpJob runner service which carries out a series of HTTP calls";
	_httpJobServiceInstaller.DisplayName = "HttpJob automatic runner";
	_httpJobServiceInstaller.ServiceName = "HttpJobRunner";
	_httpJobServiceInstaller.StartType = ServiceStartMode.Automatic;

	_httpJobServiceProcessInstaller.Account = ServiceAccount.LocalSystem;

	Installers.AddRange(new System.Configuration.Install.Installer[]
		{
			_httpJobServiceInstaller
			, _httpJobServiceProcessInstaller
		});
}

After the installation we’d like the service to start automatically. Add the following override below InitializeComponent():

protected override void OnAfterInstall(System.Collections.IDictionary savedState)
{
	base.OnAfterInstall(savedState);
	using (var serviceController = new ServiceController(_httpJobServiceInstaller.ServiceName, Environment.MachineName))
	{
		serviceController.Start();
	}
}

Without this code the service would be sitting inactive after the installation.

We’ll set one last option before we’re ready to move on. In the Properties window of a service you’ll note a tab called Recovery:

Recovery options

There are 4 options here for all failure types:

  • Take no action: if there’s an unhandled exception in the Service then it will stop and will stay inactive until restarted. This may not be optimal because if the service encounters a failure then it will just stop and wait
  • Restart the service: restart the service after an unexpected shutdown
  • Run a program: run an executable program with some parameters
  • Restart the computer: probably the most drastic recovery type

Setting the recovery type in code is not as straightforward as we’ve seen so far. We have to call the command line tool for Services, called “sc” programmatically. The process must run after the installation has been committed. The following code will set the recovery options to “restart the service”:

private void SetRecoveryOptions()
{
	int exitCode;
	using (Process process = new Process())
	{
		ProcessStartInfo processStartInfo = new ProcessStartInfo();
		processStartInfo.FileName = "sc";
		processStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
		processStartInfo.Arguments = string.Format("failure \"{0}\" reset= 0 actions= restart/1000", _serviceName);
		process.Start();
		process.WaitForExit();
		exitCode = process.ExitCode;
	}

	if (exitCode != 0)
	{
		throw new InvalidOperationException(string.Format("sc failure setting process exited with code {0}", exitCode));
	}
}

The above code will be called from an overridden method:

protected override void OnCommitted(System.Collections.IDictionary savedState)
{
	base.OnCommitted(savedState);
	SetRecoveryOptions();
}

Note the following: it requires administrator rights to change the status of a Windows service through “sc”. BTW “sc” refers to c:\windows\system32\sc.exe which allows the manipulation of Windows services in a command prompt. So it is possible that if we in the next module run the installation script then the “OnCommitted” phase will fail even if we run the script as an administrator. The reason is that the Process object itself won’t have admin rights. We’ll add an extra safety in the installation script to account for that possibility. The outcome depends on the UCL settings.

You might think that the SetRecoveryOptions method is then not needed if we do the same in the script later. I’ll leave it as it is so that you see how it can be done in code and you can decide how to set up your installation options.

We’re done with the basics. There are many other methods that you can override in the installer. Just type “override” in the editor and press Space. You’ll get a range of methods that you can override to tweak what should happen during the installation process.

Other properties

There are many other properties of a Windows service that you cannot change here. Instead you would turn to the InitializeComponent method of HttpJobRunner.cs where by default we have the current implementation:

private void InitializeComponent()
{
	components = new System.ComponentModel.Container();
	this.ServiceName = "Service1";		
}

Change the ServiceName to “HttpJobRunner” here as well otherwise messages logged to the Event Log will appear under the name of “Service1”. If you type “this.” in the editor then IntelliSense will give you all the properties that you can set. Some interesting ones are those whose name starts with “Can”, such as “CanShutDown”. You can specify if the service can be paused, shut down or stopped. In case you’re looking for a property that you cannot set through the installer then it’s a safe bet that you’ll find it here.

This will be enough of the basics. We’ll continue with the installation in the next post.

View the list of posts on Architecture and Patterns here.

Using a Windows service in your .NET project part 4: the consumer layer

Introduction

In the previous post of this series we built the application service of the demo application. We’re now ready to build the consumer layer.

For demo purposes we’ll only build a Console-based consumer where the user can insert the URLs to run and monitor the progress. In reality the consumer layer would most likely be an MVC application which can then be refreshed periodically to get the latest status. Alternatively you can turn to web sockets with SignalR to push the updates to the screen. In our example, however, a console app will suffice to reach the goal of using a Windows service.

We’ll build upon the demo app we’ve been using so far so have it ready in Visual Studio

The consumer layer

Add a new Console application to the demo solution called Demo.DemoConsumer. Add a reference to all other layers in the solution:

  • Repository
  • ApplicationService
  • Domain
  • Infrastructure

First we’ll need a method that builds an IHttpJobService that the Console can work with:

private static IHttpJobService BuildHttpJobService()
{
	IConfigurationRepository configurationRepository = new ConfigFileConfigurationRepository();
	IDatabaseConnectionSettingsService dbConnectionSettingsService = new HttpJobDatabaseConnectionService(configurationRepository);
	IJobRepository jobRepository = new JobRepository(dbConnectionSettingsService);
	IHttpJobService httpJobService = new HttpJobService(jobRepository);
	return httpJobService;
}

There’s nothing magic here I hope. We simply build an IHttpJobService from various other ingredients according to their dependencies.

Then we’ll build a simple console UI to type in the URLs to run. Insert the following method to Program.cs where the user can enter URLs and break the loop with an empty string:

private static List<Uri> EnterUrlJobs()
{
	HttpJob httpJob = new HttpJob();
	List<Uri> uris = new List<Uri>();
	Console.WriteLine("Enter a range of URLs. Leave empty and press ENTER when done.");
	Console.Write("Url 1: ");
	string url = Console.ReadLine();
	uris.Add(new Uri(url));
	int urlCounter = 2;
	while (!string.IsNullOrEmpty(url))
	{
		Console.Write("Url {0}: ", urlCounter);
		url = Console.ReadLine();
		if (!string.IsNullOrEmpty(url))
		{
			uris.Add(new Uri(url));
			urlCounter++;
		}
	}		
			
	return uris;
}

The following method will insert a new HttpJob and return the insertion response:

private static InsertHttpJobResponse InsertHttpJob(List<Uri> uris, IHttpJobService httpJobService)
{
	InsertHttpJobRequest insertRequest = new InsertHttpJobRequest(uris);
	InsertHttpJobResponse insertResponse = httpJobService.InsertHttpJob(insertRequest);
	return insertResponse;
}

Finally we need a method to monitor the job and print the job status on the screen:

private static void MonitorJob(Guid correlationId, IHttpJobService httpJobService)
{
	GetHttpJobRequest getJobRequest = new GetHttpJobRequest() { CorrelationId = correlationId };
	GetHttpJobResponse getjobResponse = httpJobService.GetHttpJob(getJobRequest);
	bool jobFinished = getjobResponse.Job.Finished;
	while (!jobFinished)
	{
		Console.WriteLine(getjobResponse.Job.ToString());
		getjobResponse = httpJobService.GetHttpJob(getJobRequest);
		jobFinished = getjobResponse.Job.Finished;
		if (!jobFinished)
		{
			Thread.Sleep(2000);
			Console.WriteLine();
		}
	}
	getjobResponse = httpJobService.GetHttpJob(getJobRequest);
	Console.WriteLine(getjobResponse.Job.ToString());
}

We keep extracting the updated HttpJob until it has reached the Finished status. We wait for 2 seconds between each iteration. After the loop we want to print the final job status.

The methods can be called as following from Main:

static void Main(string[] args)
{
	List<Uri> uris = EnterUrlJobs();
	IHttpJobService httpJobService = BuildHttpJobService();
	InsertHttpJobResponse insertResponse = InsertHttpJob(uris, httpJobService);
	MonitorJob(insertResponse.JobCorrelationId, httpJobService);

	Console.WriteLine("Main finishing, press any key to exit...");
	Console.ReadKey();
}

Run this code to see it in action. You’ll be able enter a number of URLs first. Note that there’s no extra validation so be precise and enter the full address, like “https://www.google.com&#8221;. When you’re done just press enter without entering a URL. The monitoring phase will begin:

Initial Program output with no runner yet

There’s of course nothing that carries out the job yet so the while loop never exits in MonitorJob.

We’ll start building the Windows service in the next post.

View the list of posts on Architecture and Patterns here.

ultimatemindsettoday

A great WordPress.com site

Elliot Balynn's Blog

A directory of wonderful thoughts

Softwarearchitektur in der Praxis

Wissenswertes zu Webentwicklung, Domain-Driven Design und Microservices

Technology Talks

on Microsoft technologies, Web, Android and others

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Guru N Guns's

OneSolution To dOTnET.

Johnny Zraiby

Measuring programming progress by lines of code is like measuring aircraft building progress by weight.

%d bloggers like this: