As most of you are aware, a Windows Service cannot have a GUI (Graphical User Interface) … you can’t run Forms in the Service, you can’t popup a Dialog box and you can’t get input via a Console.ReadLine()!. If you did not previously know that about Windows Services , well … now you do!
There was an interesting discussion about this last month on the MSDN forums. See the thread here:
In that thread, I went back-and-forth with a fellow named Andrew (who has since changed his user name to “Content Removed”). The end result was possibly a misunderstanding between the use of the acronyms GUI and UI, because user interaction *can* be achieved without a GUI. A Service can certainly communicate with other applications (GUI or not) via methodologies such as MSMQ or writing to / reading from TCP/IP, files or databases.
What I didn’t previously know about, and I thank Andrew for showing how to do it, was the ability to communicate with a Service via something called a “Custom Command”. The only values for a Custom Command that you can use are those between 128 and 255. Integers below 128 correspond to system-reserved values. I, personally, have not had the need to do this, but it is an interesting concept to keep in one’s “bag of tricks”. The rest of my blog post will show you how to do this. But first, let me point you to a previous blog post of mine that shows how easy it is to create Windows Services: http://geek-goddess-bonnie.blogspot.com/2013/10/easy-windows-services.html
In my blog post linked to above, you can see that I have a service host, called MyServiceHost, that inherits from System.ServiceProcess.ServiceBase. This is where you would override the OnStart() and OnStop() base methods to do stuff before and/or after starting/stopping your Services (you can “host” multiple Services here if you want to). The way to utiilze the interaction that Andrew was talking about, is to override a method called OnCustomCommand(), which takes an integer parameter (as mentioned above).
There are two parts to implementing this functionality. First, you have to add code to your MyServiceHost class. Something like this:
// You'll want to use enums for your commands, it's more readable
// Custom commands can be any value from 128 to 255
public enum ServiceCommands
{
DoSomething = 128,
DoThingTwo = 129,
// etc.
}
protected override void OnCustomCommand(int command)
{
// always run the base!!
base.OnCustomCommand(command);
if (command >= 128)
{
switch ((ServiceCommands)command)
{
case ServiceCommands.DoSomething :
// code to do something
break;
case ServiceCommands.DoThingTwo:
// code to do something else
break;
default:
// code, if you want, to indicate an invalid value
break;
}
}
}
So, that’s pretty easy, right? Next, you’ll need code to actually send those commands to your Windows Service. This code can be anywhere … it can be in a GUI application, another Windows Service, a Console app … it doesn’t matter where. All you need to know is the name of the Service (and, I believe the application needs to be running with Administrator rights). Here’s some sample code run from a Windows Form:
// Be sure you have referenced the System.ServiceProcess
// and be sure you have a using for it
using System.ServiceProcess;
// In your application, this is all you need to send commands
// to your Windows Service.
// My Service was installed as My.MessagingService
using (ServiceController srvController = new ServiceController("My.MessagingService"))
{
if (srvController.Status.Equals(ServiceControllerStatus.Running))
srvController.ExecuteCommand((int)ServiceCommands.DoThingTwo);
else
System.Windows.Forms.MessageBox.Show(@"The service is not running.");
}
That's interesting. I have been using WCF for communication between services and GUIs. Now I'm wondering that that's overkill. Do you know if there is a way to query the service for information? I see that ServiceControllerStatus can be obtained, but what about custom settings and things like that?
ReplyDeleteHi Jason,
DeleteI don't think that using WCF for communicating between services and GUIs is overkill.
I don't think that there is much you can do using the technique I described above other than to execute simple commands and it looks like it's a "one-way street" anyway (no way to query anything that needs to be returned). But I've not had the chance (or the need) to explore it any further.