Logo
Print this page

Running Composite UI Application Block inside a windows service

This was a brain-twister: not a lot of work but hard to figure out. How does one use CAB in a windows service?

Is this a reasonable requirement, a Composite UI framework in an application with no UI? Well, it is, since CAB is not only about UI… If you have a framework of components that use CAB services and need to run them in unattended mode, it would be much easier to implement CAB support in the service instead of modifying everything to run with as well as without CAB.

I’m going to present one solution that worked for me, but I believe there are other variations. Since your requirements may vary, I’ll describe the general idea so you can modify or improve it.

So, how does CAB fit within a service? There’s a couple of fundamental differences between the two: a service class needs to be derived from the framework-defined ServiceBase class while a CAB application has its own base class, WindowsFormsApplication. A service doesn’t need to have a message loop like a windows forms application does.

But the thing is, CAB doesn’t require any of the windows app features. And this is the crucial detail: CAB is simply a bunch of objects and services attached to your component. When running a CAB application, the CAB infrastructure creates all the necessary services and structures and then starts the application like it’s not different from any other windows app. It is the programmer’s task to have his components keep references  to CAB components (like WorkItem) and communicate with them. But the CAB structures are inactive and not required for proper running of a CAB application. If all of the components within it reset their properties that reference the CAB infrastructure, the whole bunch of CAB objects would probably be disposed by the CLR’s garbage collector.

In a windows application, it’s the message pump that holds the application together: when the pump stops, the application exits. In a service, its equivalent is the service object (one or more of them). You cannot substitute a message pump for a service because Windows service host expects to get a running service and nothing else. If a service object is not registered within a specified timeout period of starting the service, Windows will assume the process has hanged -even if there’s a message pump running within it.

So, obviously, one part of the solution is to put our service where the “ordinary” CAB application’s message loop was. This is the CAB application’s Start() method. When you create a windows CAB application, you need to override WindowsFormsApplication’s Start() method and start the message pump within it. Now, for the purposes of creating a service, you can create an equivalent WindowsServiceApplication class and run the service within its overridden Start() method. Since a service doesn’t have a shell, you derive WindowsServiceApplication directly from CabApplication class.

But how do we run the WindowsServiceApplication? We can do this from the Main() method: remember that running the CAB application is equivalent to running an ordinary application: after the CAB startup code creates the necessary infrastructure objects, the application proceeds to run just like any other. What we probably should do, however, is create the service instance using CAB so that everything within it can use CAB. This isn’t necessary: in a similar way to an “ordinary” CAB application, you register with CAB only the components that are CAB-aware.

Here’s the sample code (note that I haven’t tested it, this is a simplified version of the class we have in our application):

[sourcecode language="csharp"]
using Microsoft.Practices.CompositeUI;

namespace EightBit.CrmService
{
	class WindowsServiceApplicationDemo : CabApplication<WorkItem>
	{
		static void Main(string[] args)
		{
			WindowsServiceApplicationDemo prg = new WindowsServiceApplicationDemo();
			prg.Run();
		}

		protected override void Start()
		{
			// uncomment to debug service startup
			//System.Diagnostics.Debugger.Break();

			// this part ensures that the newly created crm service
			// gets attached to the CAB environment
			CrmService svc = this.RootWorkItem.Items.AddNew<CrmService>();
			System.ServiceProcess.ServiceBase.Run(svc);
		}
	}
}
[/sourcecode]


I haven’t shown the CrmService class source because it’s not different from an ordinary service – except that it now has CAB dependency properties.

Copyright © 2015 8Bit. All rights reserved.