Note: This article was originally published in June 2004
on 15Seconds.com, but it has gotten good reviews, and the techniques, I
believe, are still valid. I hope the readers of ASPAlliance find it as useful
as I have.
[Download Code]
I was hanging out with some local user group types last
night when the "Perfect Storm" (the movie based on a severe storm in
1991) came up. Since I was just talking about this very flexible Windows
service I had written, it suddenly came to me to name the article about it
"The Perfect Service." Of course, this is not to say that it is
indeed the perfect service, but all the same, I thought it sounded really good
at the time (or maybe that was the Scotch...).
In any case, this article is the first in a two part series
that covers first how to use the "perfect service" and then explores
the details of how it works. The first part covers the details of how to get
the service running and then how to add plug-n-play services to it. The second
part will explore the details, which includes the use of advanced topics such
as loading and unloading application domains, using .NET Remoting, and using
reflection.
I realize that I am not the first, nor will I likely be the
last, to attempt such a feat. In fact, I've referenced two other articles
covering more or less the same goal in the resources section. However, I
believe that this particular implementation is superior in its ease of use and
flexibility.
First off, I need to discuss terminology. This is necessary
because for some reason the English language does not offer too many
alternatives to the word "service" in the way that we mean it when we
talk about Windows services, Web services, services in service-oriented
architecture, and the like. I think the best alternative that isn't overloaded
in the IT world is "ministration," and while I'm all for
complicated-sounding words, I think I'll just stick with the industry term to
describe the concept I am referring to and use a modifier to clarify when
necessary.
The new concept I'm introducing is related to the title of
my Windows service, which is ".NET Service Manager." This Windows
service essentially provides you with drag and drop or XCOPY deployment of what
would normally have to be installed as individual Windows services in
themselves. Basically, any long-running or timed process that you want to be
run even when a user is not logged in qualifies as a candidate for a Windows service.
Unfortunately, Windows services, even in .NET, are not the
easiest thing to implement, and you have to manually install them and start and
stop them using the Services MMC snap-in. This basically involves a fair amount
of drudgery and, if you have many processes like this, can make your services
list become unfriendly.
Enter the .NET Service Manager. This application enables you
write, install, and start (using the MMC snap-in) the Windows service just
once. At that point, you are ready to do some real work, using the same (or
better) XCOPY story that, for instance, ASP.NET has. By simply applying an
assembly attribute and implementing a simple interface, you can enable any .NET
code library (DLL) assembly to run under the .NET Service Manager. I've decided
to call such assemblies "managed services," which simply means they
act like a Windows service but are managed by the .NET Service Manager.
For the most part, in this series, when I refer to a
"service," I will qualify it as either a Windows service (formerly
known as "NT service") or managed service, which indicates the kind
of service described in the previous paragraph. This article will cover the
details of implementing a managed service, so you can take away from it the
ability to use the .NET Service Manager and all the conveniences it provides
without having to know how it works. The next part will cover how all of this
is made possible.
Not only does the .NET Service Manager enable you to install
and start a service by simply copying it to the process directory, it also
enables you to update it on the fly (using the same deployment mechanism)
without needing to stop or start the service manager itself. Doing this will
not affect the other managed services because they are all running in their own
application domains, and it is not necessary to stop the service manager
itself. Similarly, you can stop an individual managed service by removing
(deleting or moving) its DLL from the service process directory-so uninstalling
is as easy as installing.
Further, you may be aware that .NET does not support config
files for code libraries. There are some good reasons behind this, but I have
found numerous occasions where having this capability would be quite handy.
Managed services are one of them, so I've enabled an easy-to-use interface for
storing settings in an app.config-like file (e.g., SampleService.dll.config)
that follows the same conventions as a typical config file with app settings.
We will also cover an example of implementing this technology.
There are only two steps required to enable your code to run
as a managed service:
- Implement the ServiceBroker.IService interface on one of
your types.
- Apply the ServiceBroker.ServiceEntryPointAttribute to your
assembly, using it to specify a display name for the service and the type
that serves as the service entry point-this must be a type that implements
the interface from Step 1.
There is an optional third step that you can use to take
advantage of the config file capabilities, but you only need this if your
managed service requires any configurable settings. All three of these will be
covered in this article.
You will, of course, also need to have a reference to the
ServiceBroker.dll assembly. It is installed with the .NET Service Manager, so
you can just reference it in the installation directory (e.g., C:\Program
Files\Littlechip\.NET Service Manager\) or copy it to some other common
assembly reference directory as demonstrated in the article "Where's My Assembly?" and reference
it there.