# WCF-services in a Windows service

Monday, August 2, 2010

Some time ago I ran into one of those problems with no obvious solution. I was on a project where I needed to use a WCF-service for a Silverlight solution. First I started out by making a service that was hosted on the IIS. That was working fine with a connection to a database, but the service was also going to open up some physical files in a specific folder on the Windows server where it was hosted. This could not be done as the service didn’t have access permissions to a folder outside of those under the service. So what to do then?

I searched for a solution to the problem and one that I found was to use Windows impersonation in the service. This “simulates” a user logged in on the server with the rights given to that user. For me this wasn’t an optimal solution for a number of reasons, first of all because it didn’t seem very secure. I quickly started to search for another way to cope with the problem.

The solution I came up with was this: I realized that as I had administrator rights to the server I could host my WCF-services in a Windows service and install it as such. In this way you can run the WCF-service outside of the IIS and run multiple services in the same Windows service as well. Another great thing about it is that if you install it right (as I will show below) you can get it to have access to the file system and it will run as a service under Windows. The example below shows you how the constructor in a Windows service can look like:

public Service()
{
InitializeComponent();

this.EventLog.Log = "Application";

this.AutoLog = true;
this.CanHandlePowerEvent = true;
this.CanHandleSessionChangeEvent = true;
this.CanPauseAndContinue = true;
this.CanShutdown = true;
this.CanStop = true;
}


What happens here is that I provide the name the service has (in Windows this will be the name of the service) and if it should use the application log under Windows to log in. I also tell it that it should log automatically to the application event log when something happens with the service and I tell it that it’s okay to stop, shutdown and pause and continue among others.

The code example below shows the Main-method of the service. As with any other Windows application this has to be provided as the starting point of the application.

static void Main(string[] args)
{
try
{
ServiceBase.Run(new Service());
}
catch (Exception ex)
{
// Some logging or error handling here...
}
}


Here I make the service run. Remember the try-catch block because if something bad happens when the service is initialized this will not make everything crash. Also notice the inheritance from the ServiceBase-class. This is what makes our class a service and is needed when we’re going to install it later also and make it run. To make the service do something in certain situations before Windows service events is fired, when it starts, stops, continues, pauses or shuts down (if the server it resides under for instance shuts down), you can override the OnStart, OnStop, OnContinue, OnPause or OnShutdown methods respectively. Up until know I haven’t shown how I combine the Windows and WCF-services, but my next code examples shows just that. What you need first of all is to make your WCF-services start when the Windows service starts. This is done by overriding the OnStart method as mentioned above and then inside this hosting the WCF-services in some service hosts and open them. I learned that a good way to be able to control every service host with the WCF-service inside is to have it declared as variables inside the class. An example on this is provided below.

partial class Service : ServiceBase
{
public ServiceHost serviceHostFirstWcfService = null;
public ServiceHost serviceHostSecondWcfService = null;
....
}


In my OnStart method I do the following:

protected override void OnStart(string[] args)
{
try
{
if (this.serviceHostFirstWcfService != null)
this.serviceHostFirstWcfService.Close();

this.serviceHostFirstWcfService = new ServiceHost(typeof(FirstWcfService));

this.serviceHostFirstWcfService.Open();

if (this.serviceHostSecondWcfService != null)
this.serviceHostSecondWcfService.Close();

this.serviceHostSecondWcfService = new ServiceHost(typeof(SecondWcfService));

this.serviceHostSecondWcfService.Open();
}
catch (Exception ex)
{
// Some exception handling...
}
}


First of all for every WCF-service I find out whether it has been initialized before. If it has, I close it so I don’t get an exception thrown when I try to open a service that is already open. Afterwards I declare the service host given the type of WCF-service that it should open and open it. As with everything else I pack it all inside a try-catch block to prevent the Windows service from crashing.

Finally you need to provide the normal configuration settings for your WCF-services inside an App.config file in the Windows service class library. The App.config file can look like the one below:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="MyNamespace.FirstWcfService">
<identity>
<dns value="Something" />
</identity>
</endpoint>
<host>
</host>
</service>
<service name="MyNamespace.SecondWcfService">
<identity>
<dns value="Something" />
</identity>
</endpoint>
<host>
</host>
</service>
</services>
</system.serviceModel>
</configuration>


There you have it! The service works fine like this and if you have provided the correct base address the service can be called from there (remember that what you provide as address in the baseAddress property as the full address for the service – so when you call it you don’t need the .svc prefix). Tip: You can provide multiple endpoints if you need multiple bindings.

The service worked great until I started to test it with the Silverlight application and realized that the service, of course, needed a clientaccesspolicy.xml file to have the right access rights for the WCF-service, but where do you put this when you don’t have your service on the IIS and you can’t just put the access policy file in the root directory? The solution for this is to understand how the Silverlight application asks for the clientaccesspolicy.xml file when calling the WCF-service. When a Silverlight application calls the service in another domain it automatically assumes that the clientaccesspolicy file is located in the root of the domain where the service resides (the base address provided in the configuration file without the service name). If it isn’t there it gives you the standard 404 error (”File not found” or something like that) when you try to call the service from the application. So if you can ”broadcast” a clientaccesspolicy.xml file on the root address this is the solution, but how to do that? The solution is to add a new WCF-service that, by using the HTTP GET-protocol stream the xml-file as a message. The interface for the new service should look like below. The UriTemplate-property of the WebGetAttribute tells what the URI should be. In our example it is the name of the clientaccesspolicy file.

[ServiceContract(Namespace = "http://YourService")]
public interface IClientAccessPolicyService
{
[OperationContract]
[WebGet(UriTemplate = "clientaccesspolicy.xml")]
Message ProvidePolicyFile();
}


Then, in the implementation of the service interface, you just open and read the clientaccesspolicy.xml file as a stream, load it into a StringReader, add this to a XmlReader, and make a new instance System.ServiceModel.Channels.Message, add your Xmlreader object to the message and return it.

    public class ClientAccessPolicyService : IClientAccessPolicyService
{
public System.ServiceModel.Channels.Message ProvidePolicyFile()
{
try
{
string fileContent = string.Empty;

fileStream.Close();

System.ServiceModel.Channels.Message result = Message.CreateMessage(MessageVersion.None, "", reader);
return result;
}
catch (Exception ex)
{
return null;
}
}
}


There you have it! When you now call your service from a Silverlight application, you will see that it gets the clientaccesspolicy.xml from the service.

After my service is done I can install it using a Windows installer or a basic console application.