Exposing WPF Windows or Windows Forms as WCF Services

Why would you want to do that, you might ask? The scenarios are not many, but picture a window’s whose whole purpose in life is to display data pumped into it by other processes. Maybe you have heard of such a thing…. one of these you might have heard of is called Performance Monitor (although that of course is not written in WCF). Or you could envision a manufacturing line monitoring application where the droid controller applications constantly send status, position, count, etc. information to the monitor app.

There is are some subtle tricks to doing this that you should be aware of if you head down this path.

First issue: Concurrency

By default, WCF answers incoming service calls on threads from the thread pool. There is a hard rule in windows programming that thou shalt not make modifications to the UI from a thread other than the UI thread. Windows Forms did not really enforce this, but stood the chance of crashing at unpredictable times (as with all concurrency problems) if you ignored the rule. VS 2005 and beyond help us out by throwing an InvalidOperationException when in debug mode if it detects that you are violating this rule. WPF makes it more rigorous by baking it into the DispatcherObject from when all UI elements derive so that any attempt to access a member of a DispatcherObject derived class from a non UI thread (if that object has thread affinity) will immediately throw an exception.

The good news here is that WCF also by default detects thread affinity when you open your service host, and if it sees that the calling thread has a SynchronizationContext (as both Windows Forms and WPF do), it holds a ref to that object so it can use it to automatically marshal the incoming calls to the right (UI) thread. This is driven by the UseSynchronizationContext property of the ServiceBehavior, which is true by default.

So basically if you do nothing in a forms app, the right thing happens and all incoming calls to the service will be marshalled to the UI thread, so those calls can be safely dispatched to the window, and you can update the UI from within the service method of the call chain that ensues.

Note that you can still run into concurrency issues if you call out from the window on the UI thread and that call chain comes back into the thread as an incoming service call or callback. Then the incoming call tries to marshal over to the UI thread, but that thread is blocked until the outgoing service call completes, which can’t happen because it is waiting for the incoming call to complete as part of its call chain… deadlock. There are a number of ways to break that deadly embrace, but that is a separate post. Thegood news again… WCF protects you better than normal .NET multithreading because calls timeout. So you will only be deadlocked until the outgoing call times out (60 seconds by default), then an exception will ensue.

Second Issue: Instancing

Because in many situations you don’t have to explicitly worry about the instancing modes of WCF, you might not think about this when setting up your window to expose it as a service. For example, you might write try to write your code like the following:

1publicpartialclassWindow1 : Window, ITalkToPeer 2 { 3ServiceHost m_host = newServiceHost(typeof(Window1)); 4public Window1() 5{ 6InitializeComponent(); 7m_host.Open(); 8} 9 10// Service method 11publicvoid SendMessage(string msg) 12{ 13m_PeerMessageListBox.Items.Add(msg); 14} 15 16privatevoid OnWindowClosed(object sender, EventArgs e) 17{ 18m_host.Close(); 19} 20 } 

In the code above, ITalkToPeer is the service contract and defines a single member SendMessage, which is implemented by the window as the service.

So what is wrong with this code? Well, if you put it together and try to run it, you will not see any messages flowing into the ListBox when clients make the calls.

If you understand the instancing modes of WCF, you can fairly quickly piece together why. Where is the data going? it won’t even matter if you make all the calls from a single client through the same proxy or if multiple clients are calling. What’s the deal?

By default, the instancing mode (ServiceBehavior.InstanceContextMode to be specific) of a WCF service is Session. That means if the same client calls through the same proxy instance, the calls will all get dispatched to the same instance of the service implementation class (which is Window1 in our example). The problem is that WCF is in charge of creating the instances, and it does so when the calls come into the service. So even though our service hosting environment is set up from within our single window instance, we just told it what type to create through the ServiceHost constructor, we didn’t give it a reference to the instance.

The fix is quite simple once this realization is made. We just have to change our instancing mode to Single (meaning singleton), and use the overloaded constructor of ServiceHost which allows us to pass in a reference to a pre-constructed singleton instance. The fixed code is shown below. The problem was that a separate window instance was being created (but not shown) for the incoming calls, so the data was just going into the object model of that non-visible window instance instead of being shown in the main window you were expecting it to.

1 [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] 2publicpartialclassWindow1 : Window, ITalkToPeer 3 { 4ServiceHost m_host; 5public Window1() 6{ 7InitializeComponent(); 8m_host = newServiceHost(this); 9m_host.Open(); 10} 11 12publicvoid SendMessage(string msg) 13{ 14m_PeerMessageListBox.Items.Add(msg); 15} 16 17privatevoid OnWindowClosed(object sender, EventArgs e) 18{ 19m_host.Close(); 20} 21 }