Click here to Skip to main content
15,860,859 members
Articles / Desktop Programming / Windows Forms

Hosting WCF services in a Windows Forms Application

Rate me:
Please Sign up or sign in to vote.
4.58/5 (22 votes)
8 Nov 2006CPOL7 min read 157.8K   7.2K   68   19
Demonstrates how to host serveral singleton services in a Windows Forms application.

Sample Image

Introduction

This article discusses about some features of the service model of WCF which is part of .NET 3.0. It demonstrates how to host multiple instances of the same service and contract in a Windows Forms application. Those services are then consumed by a client form belonging to the same application and by the same client form but created from another application. It demonstrates how to create singleton services and consume them without using any configuration file.

Background

As I was abroad and I couldn't carry with me my model train control station, I decided to write a simple simulator so I could continue to work on the bigger software I'm designing and that drives the control station.

First of all, I had to create a simple simulation of the locomotives that run on the track and that the control station can pilot. As I recently attended a WCF training, I thought that it would be a good practice to write this part using the new WCF service model. Years ago, I would have written some COM servers to simulate the locomotives, but technology has evolved, and the new service model developed for .NET 3.0 is far more powerful than was COM.

In fact, what I want to simulate is the locomotive DCC decoder. This little piece of hardware is used to control a locomotive on a track referenced by its address. The control station sends commands to change the speed, the direction, and switch the lights. It also can read the status of the device by its address. It can easily be simulated by a singleton service. There is no problem of scalability as the locomotives are going to run on a single machine and their number won't be big, neither the connections to a given locomotive. First, I wanted to use a Sharable instance for the service, but this instance mode has disappeared in the latest release of WCF…

Using the code: Service contract

Here is how the interface to simulate a simple locomotive decoder looks like:

C#
[ServiceContract]
public interface IDCCLocomotiveContract
{    
    /// Gets the running direction of that locomotive
    /// </summary>
    /// <returns></returns>
    [OperationContract(IsOneWay=false)]
    Direction GetDirection();
    /// <summary>
    /// Changes the direction of that locomotive
    /// </summary>
    /// <param name="direction">New direction</param>
    [OperationContract]
    void ChangeDirection(Direction direction);
    /// <summary>
    /// Sets the new speed value
    /// </summary>
    /// <param name="speed">Speed value (0 - 28)</param>
    [OperationContract]
    void SetSpeed(byte speed);

    /// <summary>
    /// Gets the current locomotive speed
    /// </summary>
    /// <returns>Speed value</returns>
    [OperationContract(IsOneWay=false)]
    byte GetSpeed();

    /// <summary>
    /// Switch the main light
    /// </summary>
    /// <param name="state">ON if true, OFF if false</param>
    [OperationContract]
    void SwitchLight(bool state);

    /// <summary>
    /// Gets the main light status
    /// </summary>
    /// <returns>true if ON, false otherwise</returns>
    [OperationContract(IsOneWay=false)]
    bool GetLightState();
}

Service implementation

WCF allows managing the instance behavior just by using an attribute parameter for the class that implements the service. There are three different instance modes. When using the PerCall mode, every time you call the service, you get a fresh instance. That means that all data are lost between calls. The PerSession mode maintains a session between each call until the proxy is released. The PerSession is the default mode so you don't have to specify it. Finally, the Single mode keeps the same instance of the service until the server itself is shutdown. Take note that in earlier versions of WCF (May CTP and before), the PerCall was the default mode and that there was a Sharable mode that doesn't exist anymore.

Here is how the implementation of this simple service looks like:

C#
/// <summary>
/// Implements the IDCCLocomotiveContract
/// </summary>
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
class DCCLocomotiveService : IDCCLocomotiveContract, IDisposable
{    
    const byte        
        MaxSpeed = 28,        
        MinSpeed = 0;
    protected byte m_speed = 0;
    protected bool m_light = false;
    protected Direction m_direction = Direction.Forward;
         
    public Direction GetDirection()
    {
         return m_direction;
    }

    public void ChangeDirection(Direction direction)
    {
         m_direction = direction;
    }

    public void SetSpeed(byte speed)
    {
         if (speed >= MinSpeed && speed <= MaxSpeed)
             m_speed = speed;
    }

    public byte GetSpeed()
    {
         return m_speed;
    }

    public void SwitchLight(bool state)
    {
         m_light = state;
    }

    public bool GetLightState()
    {
         return m_light;
    }
}

Demo applications

I created two simple applications. The "locomotive factory" behaves like when you put the locomotive on the track, and the "Locomotives monitor" allows watching the parameters of the locomotives. The locomotive factory is a window application that starts one server per locomotive. Each server gets an address related to the address of the locomotive. It has one endpoint for the locomotive contract. I chose this implementation because I wanted to be able to stop the server like when you remove a locomotive from the track. For testing purposes, I made it possible to the factory to control the locomotives from a control dialog box. First, I wanted to use one service and several endpoints for the locomotives, but it was not possible because all the endpoints must be created before the service host is started.

I use an XML file that contains the list of the available locomotives. Basically, I use the address and the name. A checkbox list is created from that file. When you check an element, it starts the locomotive server and creates the endpoint for it. When you uncheck the element, it stops the server.

The main user interface of the "Locomotive factory" is shown at the beginning of the article.

A button allows sending commands to the given locomotive from a dialog box. This is where I found a strange behavior. In my first draft, I was creating the ServiceHost instance in the same thread as the application. When I was calling a method on the IDCCLocomotiveContract instance I got from a ChannelFactory in the dialog box, it was failing with a timeout. However, the same call from the same ChannelFactory from another application was working.

Hosting the Service

I designed a simple class that creates the ServiceHost instance in a different thread than the one of the application. It seems that a Windows Forms application introduces restrictions due to the window messaging, and I suppose that there is some interference between this messaging and the one used by WCF. This class creates a Thread that starts the ServiceHost for my contracts and waits until the application stops it. It uses a simple pattern to stop it, with a boolean that triggers the end of the thread method. A thread must not be stopped using the Abort method, because in our case, it must close the ServiceHost. A call to Abort would let the ServiceHost open.

C#
class ThreadedServiceHost<TService, TContract>
{    
    const int SleepTime = 100;    
    private ServiceHost m_serviceHost = null;    
    private Thread m_thread;    
    private string         
        m_serviceAddress,        
        m_endpointAddress;    
        private bool m_running;    
        private Binding m_binding;

    public ThreadedServiceHost(        
        string serviceAddress, 
        string endpointAddress, Binding binding)
    {
         m_binding = binding;
         m_serviceAddress = serviceAddress;
         m_endpointAddress = endpointAddress;

         m_thread = new Thread(new ThreadStart(ThreadMethod));
         m_thread.Start();
    }

    void ThreadMethod()
    {
        try
        {
            m_running = true;
            // Start the host
            m_serviceHost = new ServiceHost(
                 typeof(TService), 
                 new Uri(m_serviceAddress));
            m_serviceHost.AddServiceEndpoint(
                typeof(TContract), 
                m_binding, 
                m_endpointAddress);
                m_serviceHost.Open();

            while (m_running)
            {
                // Wait until thread is stopped
                Thread.Sleep(SleepTime);
            }

            // Stop the host
            m_serviceHost.Close();
        }
        catch (Exception)
        {
            if (m_serviceHost != null)
            {
                m_serviceHost.Close();
            }
        }
    }

    /// <summary>
    /// Request the end of the thread method.
    /// </summary>
    public void Stop()
    {
        lock (this)
        {
            m_running = false;
        }
    }
}

Proxy for the Service

An interesting issue of my application is that I don't know by advance the number of locomotives and their addresses, so I don't use any App.Config file in my construction. I don't use any proxy as well but the ChannelFactory that allows creating a proxy on the fly. I use a NetTcpBinding which is OK for a local service and can eventually be used in Windows distributed environment. The ChannelFactory takes an interface derived from the contract of your service and the IChannel interface. It then dynamically creates a proxy to your service that you can use as if you where using the interface of your contract.

Below is the code that I use to create the proxy with my service:

C#
// Creates the corresponding endpoint
EndpointAddress endPoint = new EndpointAddress(    
    new Uri(string.Format(Constants.LocoServerBaseAddress, 
    address) + address));

// Creates the proper binding
System.ServiceModel.Channels.Binding binding = new NetTcpBinding();

// Creates channel factory with the binding and endpoint
m_dccLocoFactory = new ChannelFactory(binding, endPoint);
m_dccLocoFactory.Open();

// Creates the channel and opens it to work with the service
m_locoChannel = m_dccLocoFactory.CreateChannel();
m_locoChannel.Open();

The interface to create this proxy using the ChannelFactory is as follows:

C#
///<summary>
/// Interface used to create the Proxy for IDCCLocomotiveContract
///</summary>
interface IDCCLocomotiveChannel : IDCCLocomotiveContract, IClientChannel
{
}

The ChannelFactory is used in a dialog box that can be configured either to send commands to the locomotive service or to watch the different parameters.

Here is a copy of the two versions of that dialog box which are in fact implemented by the same code:s

Image 2 Image 3

Locomotive control is a child window form of the Locomotive Factory while the Locomotive Monitor is a child window form of the Locomotives Monitor, a simple application that connects to a locomotive service, given its address.

Image 4

Like for a real locomotive decoder, the locomotive service doesn't provide any notification. A locomotive decoder is a passive device that cannot send information to the control station by its own. In order to reflect changes in the different clients, each client must manage its own pooling.

Points of Interest

You can dig into the complete code of this simple application, and I hope it will give you some ideas of what you could do with the new service model of WCF. Of course, this is just a glimpse at this very powerful framework for Service Oriented Application (SOA). WCF is a very complete framework to develop SOA based applications in the Microsoft world. However, as it is based on open standards like WS-*, applications can be designed to interoperate with other applications that comply to those standards regardless of the technology that implements it.

Even if it is a simple demo of what can be done with WCF, this should help those who are discovering this new service model. Doing it, I discovered as few interesting points such as hosting the services in a Forms application which requires to use a specific thread for the service. I also found out that if you need to start and stop individual instances of the same service, you cannot use multiple endpoints, but must use a host for each of your instance.

Another interesting point is the creation of dynamic hosts and proxies as their addresses depend on a list and is unknown by advance.

License

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


Written By
Architect Connect In Private
Singapore Singapore
Software Architect, COM, .NET and Smartcard based security specialist.

I've been working in the software industry since I graduated in Electrical and Electronics Engineering. I chose software because I preferred digital to analog.

I started to program with 6802 machine code and evolved to the current .NET technologies... that was a long way.

For more than 20 years I have always worked in technical positions as I simply like to get my hands dirty and crack my brain when things don't go right!

After 12 years in the smart card industry I can claim a strong knowledge in security solutions based on those really small computers!
I've been back into business to design the licensing system for the enterprise solution for Consistel using a .NET smart card (yes they can run .NET CLR!)

I'm currently designing a micro-payment solution using the NXP DESFire EV1 with the ACSO6 SAM of ACS. I can then add a full proficient expertise on those systems and NFC payments.
This technology being under strict NDA by NXP I cannot publish any related article about it, however I can provide professional consulting for it.

You can contact me for professional matter by using the forum or via my LinkedIn profile.

Comments and Discussions

 
QuestionThread update Pin
Abbathsin8-Jul-20 16:55
Abbathsin8-Jul-20 16:55 
QuestionAppreciation Pin
Stephen Halverson16-Nov-14 7:58
Stephen Halverson16-Nov-14 7:58 
Questionnice sample... wish there was an update more recent than 2006 Pin
mkamoski31-Jan-14 4:00
mkamoski31-Jan-14 4:00 
Questionwhere is the code to download - I get a not found error when I try. Pin
dholsop11-Nov-13 8:39
dholsop11-Nov-13 8:39 
SuggestionCreating a new thread for hosting the service is a bad practice, should use ServiceBehaviorAttribute on the service. Pin
matmsoft3-Jun-13 0:49
matmsoft3-Jun-13 0:49 
GeneralRe: Creating a new thread for hosting the service is a bad practice, should use ServiceBehaviorAttribute on the service. Pin
orouit14-Jun-13 10:38
professionalorouit14-Jun-13 10:38 
QuestionGreat article with concise, easy to follow example Pin
johnbMA19-Apr-13 12:12
johnbMA19-Apr-13 12:12 
GeneralMy vote of 5 Pin
Minghang15-Apr-13 7:32
Minghang15-Apr-13 7:32 
QuestionThank you for this article. Pin
X3n0Byt318-Jul-12 5:44
professionalX3n0Byt318-Jul-12 5:44 
Smile | :)
GeneralMy vote of 5 Pin
SinisaRuzin30-Mar-11 8:28
SinisaRuzin30-Mar-11 8:28 
GeneralService & Client in Same Application - TimeoutException [modified] Pin
J0J0_12-Jul-08 13:16
J0J0_12-Jul-08 13:16 
GeneralProblem when running Host and Client in 2 different machines Pin
vijayarock9-Jan-07 17:47
vijayarock9-Jan-07 17:47 
GeneralRe: Problem when running Host and Client in 2 different machines Pin
orouit10-Jan-07 9:01
professionalorouit10-Jan-07 9:01 
QuestionDistrbuted? Pin
Bo Hansen2-Jan-07 0:40
Bo Hansen2-Jan-07 0:40 
AnswerRe: Distrbuted? Pin
orouit2-Jan-07 10:20
professionalorouit2-Jan-07 10:20 
GeneralRe: Distrbuted? [modified] Pin
Bo Hansen2-Jan-07 20:33
Bo Hansen2-Jan-07 20:33 
GeneralRe: Distrbuted? Pin
orouit3-Jan-07 3:07
professionalorouit3-Jan-07 3:07 
GeneralRe: Distrbuted? Pin
Bo Hansen3-Jan-07 3:09
Bo Hansen3-Jan-07 3:09 
GeneralNice Pin
oleg_cherkasenko26-Nov-06 10:03
oleg_cherkasenko26-Nov-06 10:03 

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.