Introduction
.NET Windows service is one of the most popular types of the Windows projects. And
yet Visual Studio offers an inconvenient template for it. The main problem with
this template in my opinion is that generated code cannot run as console
application for convenient debugging. But this is not the only problem. Dealing
with Windows service, the developer normally needs to implement a more adequate thread
model than the provided by the Visual Studio template. It would be also nice
to manage the service and see its current status with icons and pop-up menu in
the system tray. This article and code will address all these problems.
Background
As it was stated above, good practice of using Windows service
implies collaboration of the service itself with its control from the system
tray. Since windows service does not have visual aspect (and in fact even runs
in different from normal applications desktop) another application
should ensure proper indication of the service current status in system tray.
In our sample this additional application is written in WPF. Normally this additional
"tray-targeted" application is running in the same machine with the Windows
service. Thus dual WCF with IPC (net.pipe) binding seems to be a natural choice
for communication between the two processes where Windows service acts as a
server.
Many developers (including me) particularly like when code
intending to solve practical problems can be reused easily. For most
of the practical cases the entire code provided in this article may be used
almost as it is (changing text variables and names of modules only) with just
few
places to put specific Windows service functionality. All places in the
SampleWinService project that
may require changes are placed in appropriate regions entitled
DIFFERS FOR EACH SERVICE.
User interface of the tray-target application provides appropriate icon indicating current status of the service
and pop-up menu which in activated by mouse right click over the icon:
Service is not running
Service is running
Please note that icon indicating current service state is updated with certain
delay after the service state has been changed.
Design
The Windows service SampleWinService possesses the following
features:
Capable of running either as console application or as Windows service,
Being used as Windows service enables self-installing and self-uninstalling,
Act as server for "useful" clients and WPF tray-targeted
application by hosting WCF dual service object,
Provide command-based working paradigm operating as following. Client application
sends commands via WCF communication. The commands are queued by WCF service
object to command queue. Commands are dequeued and processed by a dedicated working thread running in a
loop. Result of the processing is sent to the calling useful client (if
required), whereas current Windows service status is sent to the WPF
tray-targeted application. Both result and status are sent as callbacks using
WCF dual mechanism. Dual WCF is particularly useful in cases when number of
useful clients in not very high and they reside within the same LAN with the
service machine. Normally the tray-targeted application runs in the same
machine with Windows service. So in this case dual WCF with IPC (net.pipe)
binding is also a good choice for
communication.
In our sample, external applications providing commands cause the service to
perform useful work. In real life there can be also other sources of useful
command, such as input from hardware, drivers, timers, etc.
Design of our SampleWinService
is based on the following reasonable suppositions:
Tray-targeted GUI application runs in the same machine with the service,
Limited number of clients of the Windows service, and
No firewall between the service and all its clients.
The first supposition allows tray-targeted application to use asynchronous notification about
changes in the service status provided by the
System.
Management.
ManagementEventWatcher
class. The second and the third suppositions justify usage of dual WCF service.
Of course, the above suppositions are not mandatory and are made just for
simplicity. We will briefly review other cases below, in the Discussion chapter
of this article.
Block-diagram of the system is depicted below:
Windows Service
Windows service contains WCF service object which accepts calls from useful clients and tray-target client
sending commands to the service. The WCF part is implemented in ComminicationSvc project in the Libraries\ServiceLibs
folder. Interface
ICommSvc
allows clients to send commands of type
Cmd to the service. The interface is implemented by type
CommSvc
. The class
Cmd
contains command name and parameters, as well as information about client who sent the command.
Interface
ICallback
constitutes contract for clients' callback.
For client convenience the WCF service supports three endpoints for
CommunicationSvc.ICommSvc
contract with
netNamedPipeBinding
,
netTcpBinding
and
wsDualHttpBinding
bindings.
Since, according to the above suppositions, our Windows service does not have too many clients and the clients
are available for back notification, the service can process clients' commands in
a separate worker thread running in a loop. Project CommunicationSvc provides singleton the class
CommandQueue<T>
for synchronized command queue parameterized
with the
Cmd
command type. The method
CommSvc.Command()
inqueues incoming commands for processing.
Dequeueing and processing of the commands are performed in the main Windows
service process SampleWinService. Unnamed method of type
PeriodicProcessingDelegate
in
WorkerThread.StartThread()
dequeues commands and processes them with static method
CommandProcessor.Process()
. This method is responsible for actual processing of commands.
It is the only part of the service which varies according to actual task of the
given Windows service.
After processing methods
SendStatusToAllClients()
and SendReport()
of class
SampleWinSvc send status and result of processing to clients as asynchronous callbacks.
Clients
Both tray-targeted and useful clients use client part (proxy) of WCF. To share
the same code between different types of clients, it is placed in a separate
assembly ClientLib. In addition, tray-targeted
client uses specific NotifyIconWpf assembly
developed by Philipp Sumi ([1], www.hardcodet.net)
providing infrastructure for tray icon and pop-up menu in WPF applications. Both
tray-targeted and useful clients implement interface
ICommSvcCallback
(corresponds to
ICallback
interface on the server side) to receive notification calls from the service.
Interestingly enough, in some cases the tray-targeted GUI application may be
used even in stead of Windows service, e. g. when you need to run this
application to capture a screenshot which is not possible for Windows service
running in different desktop, or just for the sake of simplicity in for
non-mission-critical tasks.
Code Sample
In our sample Windows service is
presented with SimpleWinService project, and projects IconClient
and TestClient represent icon-targeted and useful clients respectively.
Demo
To run demo we have to perform self-registration of the
SimpleWinService
Windows service. Run file
SimpleWinService_Install.cmd (which starts
SimpleWinService.exe with argument /install) for this purpose.
Than start IconClient.exe and use tray icon and connected to it pop-up
menu to control
SimpleWinService Windows
service and observe its status.
Discussion
Design described above is applicable to
many practical cases of usage Windows services. In case of firewall between the
service and its client in stead of dual WCF other approaches may be used, like
for instance
technique of "smart polling" (described e. g. in
[2]) when sever holds client's
repeatable polling calls on waitable object until either notification about the
server event needs to be send to client or certain timeout elapsed. More
sophisticated messages queuing and processing mechanism may be taken e. g. from
[3].
Conclusions
This article presents solutions for Windows
service general design by providing uniform technique for such widely desirable
features as possibilities to run it as console application to facilitate
debugging, self-installing and -uninstalling, providing description,
queuing and processing of messages, as well as the service status
observation and control with icon and pop-up menu placed in the system tray. The
code accompanying this article may be reused almost as is (with just minor
changes at few places marked in code by a specific regions) as a template for
virtually any Windows service. I do hope that my fellows CodeProjecteers will
find it useful when it comes to Windows service developing.
References
[1] Philipp
Sumi.
WPF
NotifyIcon. CodeProject.
[2] Igor Ladnik.
Push Messages in RESTful WCF Web Application with Comet Approach.
CodeProject.
[3] Igor Ladnik.
Tiny Framework for Parallel Computing.
CodeProject.