Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

233 lines
8.6 KiB

SERVICE.TXT
This document contains information on how to use the CNtService class to
produce a Win32 Service Executable. (6/24/96 a-davj)
This is a simplified version of the CService class which was originally
done by Ray McCollum. It is simpler chiefly because it is now the
responsibility of the class user to manage their threads and the
class does not rely on any SMS diagnostics.
=========================================================================
1.1 A minimal conversion
To produce a service from a normal EXE with the minimum work, you must
derive a new class from CNtService and override the WorkerThread() and Stop()
virtual functions:
class MyService : public CNtService
{
public:
virtual void WorkerThread(); // Your component goes here
virtual void Stop(); // Your component goes here
};
void MyService::WorkerThread()
{
...loop doing useful work until signaled by the Stop function.
}
void MyService::Stop()
{
...Signal the thread running in WorkerThread to return from that
...routine
}
The WorkerThread() function is the entry point for your code. You can
branch out to normal non-object functions, or whatever. It is the job of
your Stop() function to signal the thread running in the WorkerFunction
to return and when that thread returns, the service will terminate properly.
Execution of the service is begun by using the Service Control Manager
APIs in another program, the Control Panel or else using NET START/NET STOP/
NET PAUSE/NET CONTINUE from a DOS box. When the user types
NET START MYSERVICE
...your service will attempt to start.
Note that you must have added your service to the SCM (Service Control
Manager) data base before you can start it. There are various ways to add a
service though I found the SC.EXE to be quite useful and it can be found
via MSDN.
Your main() entry point might be rewritten along the following
lines:
void main()
{
MyService Sample;
Sample.Run(TRUE, "MYSERVICE"); // TRUE = run as a Service,
// FALSE = run as a normal EXE
}
When Run() is called, you can make the decision as to whether you are
running as a normal EXE or a service via the first argument. The
second argument is the service name. This can be any length and is not
related to the .EXE name itself.
After Run() is invoked, then WorkerThread() is called to actually do
the work of the service. [Run() and WorkerThread() are actually separate
threads at this point, although Run() will not return until the service
stops.]
When the user types NET STOP MYSERVICE, then the Stop() function
is called and it signals for the WorkerThread() function to
return which then results in the service being stopped.
=========================================================================
1.2 Supporting Pause, Continue, and custom control codes.
If you want to support suspending your service by NET PAUSE MYSERVICE and
resuming it by NET CONTINUE MYSERVICE, then you must override the Pause()
and Continue() virtual functions and call the SetPauseContinue()
function with a TRUE argument.
It is the job of your Pause() and Continue() functions to do whatever is
right for your service. In some cases that might mean actually suspending
and resuming threads, or it could mean that the threads will just not perform
certain functions.
User codes can be accepted by overriding the UserCode() function.
The UserCode() function is only valid if the program controlling
the service has been coded to send user-defined control codes to the
service. The command-line based NET functions have no facility for this.
Basically, a specially coded control program can send codes other than
STOP, PAUSE, CONTINUE or START. These codes must fall in the subrange
128..255 and the code being sent is in the DWORD argument.
The meaning is user-defined. Since there is no response mechanism
back to the sending process, this does not seem terribly useful. These
codes are ignored if UserCode() is not overridden.
Note that the original CService class did handle some specific UserCodes
and this class does NOT.
===========================================================================
1.3 Logging support.
The service wrapper code only logs unexpected errors. By default, it adds
them to the NT event log when running as a service and to stdio when running
as an executable. It does this through a virtual function named Log() which
can be used for your code, or can be overridden if there is some other logging
mechanism that is desired.
=========================================================================
1.4 Timing Issues.
Functions such as Pausing, Continuing and Stopping must complete in
under 20 seconds. A delinquent Pause or Continue isn't too serious since
that will only cause a error for the NET xyx command. However, the system
will kill any service that is too slow in stopping.
The Initialize() function is called during NET START, but before your
worker thread begins execution. Again, this must return within 20
seconds. In most cases, you will not need this and can do initializations
in WorkerThread itself. Any command-line arguments passed in to the
service are available from the parameters. These are only valid if the EXE
is executing as a service; if you are executing as a normal EXE, then process
the command line arguments from main() instead, since these will be NULL.
In general, the command line arguments don't tend to be terribly useful for
services.
========================================================================
1.5 Non-service EXE execution.
If Run() is called with FALSE, then the service behaves as a normal
multi-threaded EXE. The behavior is still basically the same as for a
service: WorkerThread() should be the effective entry point for the work
to get done. However, there will be no NET STOP, PAUSE , User Codes, or
CONTINUE requests coming in, so your Pause(), Continue(), Initialize(),
UserCode and Stop() functions will never be called.
=========================================================================
1.6 Service Names
Service names can contain any characters if you use the Win32 APIs or
service control classes. However, the NET commands only support normal
filename type characters. It is best to use alphanumeric characters with
optional underscores.
=========================================================================
1.7 An additional example and debugging hints.
This sample uses the command line arguments to determine if the program
should run as a service. It also handles Pause and Continue and it uses
Initialize.
This sample is also setup to INTENTIONALLY CAUSE A BREAK! The WorkerThread
routine has some logic where by an INT 3 is caused 2 minutes into the run.
This brings up an exception box which gives you the oportunity to debug the
service even if it was automatically started by the system. Naturally you
still need to be running a debug build.
This code is a bit simplistic in that it uses simple variables
to communicate pause and stop and a real production program would probably
protect these variable via Mutex or use Signal(s).
class MyService : public CNtService{
public:
MyService();
DWORD WorkerThread();
void Stop(){bRun = FALSE;return;};
void Pause(){bContinue = FALSE;return;};
void Continue(){bContinue = TRUE;return;};
BOOL bRun, bContinue;
};
int main(int argc, char ** argv)
{
MyService svc;
svc.SetPauseContinue(TRUE);
if(argc > 1 && _stricmp(argv[1],"/EXE") == 0)
{
// run as console app. Note that Initialize will get the /EXE arg
svc.Initialize(argc, argv);
svc.Run("doesntMatter!", FALSE);
}
else
svc.Run("MyService", TRUE);
return 0;
}
MyService::MyService()
{
bRun = bContinue = TRUE;
}
DWORD MyService::WorkerThread()
{
int nCount = 0;
while(bRun)
{
if(bContinue)
{
MessageBeep(0);
}
Sleep(3000);
if(nCount == 40)
_asm int 3;
nCount++;
}
return 0;
}
=========================================================================
2.1 LIMITATIONS on the current implementation.
A. Only one service is supported per .EXE. This limitation can be removed,
however any having more than one service per EXE would require the
service to run only in the Local Service account which is generally
not acceptable.