One of the cool things aboutWCF is that it doesn't need to be hosted in IIS. Of course, hosting in IIS is the easy way to do things but there are times when you want to run a service but you don't want to expose it to the world. Today I worked through some of the issues around hosting a WCF in a managed Windows Service so I am going to put down some notes about the experience.
First, my goal. I wanted to expose a service that will be consumed by two different web sites but I don't want the service exposed to the world. My service will be exposed over TCP/IP or the Net.Tcp binding.
My first step was to create a WCF Class Library which actually contains my WCF service contract. There is no App.config associated with this project since it is just a .dll and any App.config you include will just be ignored. All this library needs is your Data and Service contracts along with the actual service code that implements these.
The next step was to create a Windows Service that will host this WCF service. Main from this service looks pretty simple:
static class Program {
static void Main() {
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[] { new Service1() };
ServiceBase.Run(ServicesToRun);
}
}
Just creates an array of services, adds a new Service1 (which is my Windows Service class, not the WCF class) and then runs the services.
My Service1 class is my actual Windows Service, again, not my WCF service. I think I could have used an App.config here but I decided to hard code the end points for my WCF into this service. My thinking is that if this ever changes I am probably going to recompile or remove this service anyway. I still won't have to touch my WCF Class Library though to host it somewhere else.
public partial class Service1 : ServiceBase {
public Service1() {
InitializeComponent();
}
private ServiceHost serviceHost;
protected override void OnStart(string[] args) {
serviceHost = new ServiceHost(typeof(MyWcfService));
Binding binding = new NetTcpBinding();
string address = "net.tcp://localhost:9000";
Binding mexBinding = MetadataExchangeBindings.CreateMexHttpBinding();
string mexAddress = "http://localhost:8000/MEX";
ServiceMetadataBehavior mexBehavior = new ServiceMetadataBehavior();
mexBehavior.HttpGetEnabled = true;
mexBehavior.HttpGetUrl = new Uri(mexAddress);
serviceHost.Description.Behaviors.Add(mexBehavior);
serviceHost.AddServiceEndpoint(typeof(IMyWcfService), binding, address);
serviceHost.AddServiceEndpoint(typeof(IMetadataExchange), mexBinding, mexAddress);
serviceHost.Open();
}
protected override void OnStop() {
serviceHost.Close();
}
}
First I create my serviceHost... this is the guy who is actually hosting my WCF service. Then I create my bindings. I created two... the net.tcp one is the on that I am going to be listening on for client requests. The mexBinding one is to expose my WSDL definition. I decided to just do that on HTTP localhost to make things easy... then of course quickly discovered a problem with doing this in Vista.
It turns out that Vista has extra security for allowing anyone to communicate over HTTP that isn't IIS. I kept getting an "AddressAccessDeniedException" which (eventually) I found out was caused by lack of permissions. The symptom was that my service would start... and then just stop itself. Fortunately there were plenty of messages in my EventViewer to help me track down the problem. Below is a link to the article I used to figure out how to set my ACLs properly. Basically it boils down to running this in the command line:
netsh http add urlacl url=http://+:8000/ user=everyone
Yes... I know... I really shouldn't add "everyone" but you get the idea. You could just supply the normal user name there instead of everyone. Incidentally the + sign is a wildcard to indicate that all URLs at that port will be allowed.
Back to the code though. After I added my bindings I needed to add my Mex behavior. I missed this piece on the first go-round so don't forget this step!
The last thing in this code is to add your service endpoints to your service and then open the service. Notice that the OnStop() method is where you close the service. These are the methods that are called when you start / stop a service in the MMC.
Finally, I added an installer so that my Windows Service could be installed.
I did this the quick-n-dirty way but it worked. In the Design View of the Service1 class, right click and choose "Add Installer".

This gave me a ProjectInstaller class in Design View that had serviceInstaller1 and serviceProcessInstaller1.

Here you can open the Properties pane of the serviceInstaller1 and change the Display and ServiceName properties. You also will probably want to change one property on serviceProcessInstaller1, the Account property. I ran mine as the NetworkService account.
Once you have all of this in place, you just build and you are ready to use InstallUtil to install your Windows Service that is hosting your WCF service.
Following are some links that I used to help me figure this thing out.
Links: