Ever tried to write an NT service ? :->
One of my very latest projects was to develop a CORBA server running
as an NT service. Since one of my next projects will be an NT service too,
I let my brain work a little harder to get it ready for as many programs
as needed. The result of this was the CNTService class, that I want to
share with the codegurus 🙂
With this class it is very simple to create an NT service. Just derive
your own class and override the “Run()” and “Stop()” pure virtual members.
All of the hard bits are done by the class.
There are several virtuals to overload (multi-threading, pausable services
and more).
The class accepts a set of command-line parameters. For instance you
can pass the -d switch to let it run as a console process. This makes debugging
much easier.
Furthermore the class has abilities to (de)install the service. This
is usually a command-line switch too (but one can customize this :-).
CNTService compiles with and without UNICODE settings.
Now lets have a closer look at how you can build your own service.
Follow these steps (assuming you’re using VC++ 5.0):
-
create a new project workspace of type “console application”
-
copy the files of the NTService package into the directory of your new
project
-
insert “NTService.cpp” and “NTServiceEventLogMsg.mc” into your project
-
open the Project->Settings … dialog and fill it out. See the image
below. It says more than I could write 🙂
-
compile the file “NTServiceEventLogMsg.mc”. This will produce the files
“NTServiceEventLogMsg.h” and “NTServiceEventLogMsg.rc”.
-
insert the generated RC file into your project.
-
derive your own class from CNTService and override at least the two methods
“Run()” and “Stop()”
-
write a simple “main()” to start up your program’s functionality 🙂 (You
can use the sample project for a good starting point)
-
Once your project compiles without errors, you can immediately start it.
To do so, you should open the Project->Settings … dialog once
again. Select All Configurations and than check the line with the
name of your project. Activate the tab Debug and insert the -d
switch in the field Program arguments:
Now you should be able to run and debug your service as a console program.
You can use either Ctrl-C or Ctrl-Break to stop the service (that would
simulate a Stop request from the service control manager)
How to fill out the settings of the file NTServiceEventLogMsg.mc
Now lets try to start the service as a real NT service (assuming you
use the sample project):
-
Log on as an administrator, if your account isn’t in the administrators
group (this will make life somewhat easier)
-
Start the program with the switch -i. This will install your service.
-
If the last step completes successfully, you can open the control panel
and start the Services applet. Search for the display name of your
service and select it (Very Simple Service if you try the sample).
-
Press the Startup … button. Have a look at the Log On As:
box. For now you can let the check at System Account. If you try
the sample, make sure you check the Interact With The Desktop box.
This is necessary, since the sample uses the MessageBox() function (which
interacts with the desktop). If you don’t check this box, the sample will
not run properly and will hang up itself; thus you’re unable to stop the
service again ! With a little bit of experience one can enhance the sample,
so that it will install the service with this box already checked, but
I wanted to keep the sample as simple as possible, so I leave it to you
to implement that. 🙂
The sample project implements a very simple service. The derived class
(declaration and implementation) and the main() function are all in the
same file (main.cpp). This file is less than 100 lines long (strip off
all the comments and it will fit on one page of printing !)
The service only pops up a message-box every 10 seconds.
For more information have a look at CNTService.h and – of course – the
sample. I think it is well commented (let me
know, if you don’t think so)
-
Added two more switches to handle command line arguments: -e will force
a running service to stop (corresponding method in this class: virtual
BOOL EndService();) and -s will force the service to start, if it is not
already running (method: virtual BOOL StartupService())
-
Todd C. Wilson added support for
Win95. This enables you to create a service like application for Windows
95 (a socalled “Faceless Application”). Like a service, such an application
will be started automatically by the system and will survive a user’s logoff.
My own experience with this kind of applications are not very great: Yes
I could logoff, but the system hung when I tried to logon again. Furthermore
BoundsChecker 5 (yes – I always test my software with this great tool 🙂
said, that the following sentence returns an invalid pointer (exactly “returned
pointer is not a pointer to a function”):
typedef DWORD (WINAPI *fp_RegServProc)(DWORD
dwProcessId,DWORD dwType);
fp_RegServProc fncptr=NULL;
// …
HMODULE hModule = ::GetModuleHandle(TEXT(“kernel32.dll”));
fncptr=(fp_RegServProc)::GetProcAddress(hModule,
“RegisterServiceProcess”);
if (fncptr!=NULL)
(*fncptr)(0, RSP_SIMPLE_SERVICE);
If the last line is reached, BoundsChecker warns,
that fncptr
does not point to a function. However, the sample program continues execution
without an (serious) error.
You should keep this problem in mind, if you
plan to develop a faceless application for windows 95.
The sample project offers a configuration “Win32
Win95 Debug”, that you can use to try this new feature.
Microsoft recommends to write an NT service as a console application .
However, it is possible to write such a service using MFC, too. To do so,
it isn’t necessary to create an MFC Wizard Application (although
it is possible of course, but you have to
remove a lot of code, because normally you don’t need the doc/view
concept), but a simple Win32 application is just enough. Make sure you
select Using MFC in a shared DLL from the General tab of
the Settings dialog of your project. Furthermore, you have to select
Multithreaded DLL / Debug Multithreaded DLL in the C/C++
tab, section Code Generation field Use run-time Library.
If you’re using a precompiled header, make sure you don’t #define
VC_EXTRALEAN, because the MFC based service needs parts of the “rarely
used stuff”, that this macro would exclude from the windows headers!
I’ve added a new project MFCService as part of the workspace
of the sample service to demonstrate this. Just try it out and/or use it
as a starting point for a more complex service. The service (installation/removal
etc). works exactly as described in the sections above.
CNTService consists of three files:
NTService.h
NTService.cpp
NTServiceEventLogMsg.mc
Download Source 14KB
23KB
Note that the file NTServiceEventLogMsg.mc is copyrighted by Telic
Software International B.V. Many thanks to Telic for providing it in the
public domain!
Date Last Updated: February 5, 1999