Click here to Skip to main content
15,868,141 members
Articles / Programming Languages / C#

Host WCF as Windows Service without Installing

Rate me:
Please Sign up or sign in to vote.
4.79/5 (17 votes)
5 Dec 2009CPOL3 min read 37.9K   43   5
Learn how to host WCF as a Windows service (without using a web project) and how to code it so that you can test interactively without installing the service.

I am working on a project that involves a centralized UI that then coordinates with agents to send out instructions. The agents will be WCF endpoints and to ease deployment/minimize the install footprint, they will install as windows services with a nice installer rather than hanging onto an IIS webspace directly.

In this post I want to walk you through the process of creating that WCF host programmatically, as well as a small little tweak that will allow you to run and test without having to install the service. It's actually quite easy to do but it's one of those things that can produce an "ah-hah!" moment if you haven't done it before.

To start with, let's create a simple service and operation contract. Create a new interface, call it IHelloWorldService, and add a method, like this:

C#
[ServiceContract(Namespace="http://wintellect.com/sample")]
public interface IHelloWorldService
{
    [OperationContract]
    [FaultContract(typeof(Exception))]
    string Hi();
}

You'll have to add references and usings for System.ServiceModel.

Next, implement the interface:

C#
public class HelloWorldService : IHelloWorldService
{
    const string REPLY = "Hello, world."; 

    public string Hi()
    {
        return REPLY; 
    }

}

That's all it takes to make your WCF service. Now we just need a way to host it. Add another reference to System.ServiceProcess. Create a new class called HelloServiceHost and derive it from ServiceBase. Now we'll get a little interesting:

C#
public class HelloServiceHost : ServiceBase
{
    const string CONSOLE = "console";

    public const string NAME = "HelloWorldService"; 

    ServiceHost _serviceHost = null;

    public HelloServiceHost()
    {
        ServiceName = NAME; 
    }

    public static void Main(string[] args)
    {
        if (args.Length == 1 && args[0].Equals(CONSOLE))
        {
            new HelloServiceHost().ConsoleRun();
        }
        else
        {
            ServiceBase.Run(new HelloServiceHost());
        }
    }

    private void ConsoleRun()
    {
        Console.WriteLine(string.Format("{0}::starting...",GetType().FullName));

        OnStart(null);

        Console.WriteLine(string.Format("{0}::ready (ENTER to exit)", GetType().FullName));
        Console.ReadLine();

        OnStop();

        Console.WriteLine(string.Format("{0}::stopped", GetType().FullName));
    }

    protected override void OnStart(string[] args)
    {
        if (_serviceHost != null)
        {
            _serviceHost.Close();
        }

        _serviceHost = new ServiceHost(typeof(HelloWorldService));
        _serviceHost.Open();
    }

    protected override void OnStop()
    {
        if (_serviceHost != null)
        {
            _serviceHost.Close();
            _serviceHost = null;
        }
    }
}

Let's break it down. We're derived from service base, and need a service host to actually present the WCF end point. In the constructor, we set the service name. In the Main class, which is called for both console applications and service applications, we check the arguments. If you pass in "console" as an argument, it will create a new instance and run it directly. Otherwise, it calls into the ServiceBase to run as a Windows service.

The ConsoleRun is stubbed out to give you a nice message that it is starting, then manually call the start event and wait for a line to be entered. When you hit ENTER, it will close the service and shut down. This is what enables us to take the generated executable, and call it, like:
HelloWorldService.exe console
and then debug interactively without having to install it as an actual service.

The OnStart and OnStop overrides do basic housekeeping for registering the types of the endpoints that will be active (this one process could host several endpoints, if you so desired) and shutting down when finished.

Of course, you'll need to add an app.config file and come up with your end point name. It can run on existing IIS ports, but must have a unique URL. I set mine up like this:

XML
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="HelloWorldService.HelloWorldService" behaviorConfiguration="HelloWorldServiceBehavior">
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:80/helloworld"/>
          </baseAddresses>
        </host>
        <endpoint address=""
                  binding="wsHttpBinding"
                  contract="HelloWorldService.IHelloWorldService"/>
        <endpoint address="mex"
                  binding="mexHttpBinding"
                  contract="IMetadataExchange"/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="HelloWorldServiceBehavior">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="False"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

Once this is in place, you should be able to compile, browse to the directory it is in, and run the program with the "console" argument (no dashes or anything special). Once it is waiting for a keypress, go to a web browser and navigate to http://localhost/helloworld?wsdl and you should see the WSDL for your service. At this point you could hook it up to a test harness, use SoapUI, or about any other method for testing and start doing things with the service.

Of course, eventually the goal is to allow it to be installed as a Windows service. For that, add a reference to System.Configuration.Install. Add a new class and call it ProjectInstaller. Derive the class from Installer. The class will look like this:

C#
[RunInstaller(true)]
public class ProjectInstaller : Installer
{
    const string DESCRIPTION = "Hello world service host.";
    const string DISPLAY_NAME = "Hello World Service";

    private ServiceProcessInstaller _process;
    
    private ServiceInstaller _service;

    public ProjectInstaller()
    {
        _process = new ServiceProcessInstaller();
        _process.Account = ServiceAccount.LocalSystem;
        _service = new ServiceInstaller();
        _service.ServiceName = HelloWorldService.NAME;
        _service.Description = DESCRIPTION;
        _service.DisplayName = DISPLAY_NAME; 
        Installers.Add(_process);
        Installers.Add(_service);
    }
}

Now you can navigate to the command line and use installutil.exe to install it as a bona fide Windows service.

I'm not including a demo project because this should be fairly straightforward and I've included 100% of the code in this post ... I bet you might not have known hosting a WCF service outside of an actual web project was so simple!

Jeremy Likness

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Program Manager Microsoft
United States United States
Note: articles posted here are independently written and do not represent endorsements nor reflect the views of my employer.

I am a Program Manager for .NET Data at Microsoft. I have been building enterprise software with a focus on line of business web applications for more than two decades. I'm the author of several (now historical) technical books including Designing Silverlight Business Applications and Programming the Windows Runtime by Example. I use the Silverlight book everyday! It props up my monitor to the correct ergonomic height. I have delivered hundreds of technical presentations in dozens of countries around the world and love mentoring other developers. I am co-host of the Microsoft Channel 9 "On .NET" show. In my free time, I maintain a 95% plant-based diet, exercise regularly, hike in the Cascades and thrash Beat Saber levels.

I was diagnosed with young onset Parkinson's Disease in February of 2020. I maintain a blog about my personal journey with the disease at https://strengthwithparkinsons.com/.


Comments and Discussions

 
GeneralMy vote of 4 Pin
RS (SE)24-Apr-13 2:02
RS (SE)24-Apr-13 2:02 
Questionnice. Pin
Manas Bhardwaj2-Jun-12 6:04
professionalManas Bhardwaj2-Jun-12 6:04 
GeneralHelpfull Pin
Eric Bogaerts9-Dec-09 10:05
Eric Bogaerts9-Dec-09 10:05 
Nice to test services and you give insight by your cleanly written article.
GeneralNice Article.,. Pin
Srinath Gopinath8-Dec-09 17:24
Srinath Gopinath8-Dec-09 17:24 
GeneralVery nice Pin
PeterFortuin8-Dec-09 5:09
PeterFortuin8-Dec-09 5:09 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.