Wessel Troost said:
Every stopping service calls ExitProcess. The other option is the SCM
calling TerminateProcess(), and the SCM doesn't do that, obviously. A
third option would be to generate a Win32 Structured Exception (f.e.
segfault) to leave the process.
If there are multiple services in a process, the process would only call
ExitProcess when all of its services are stopped.
Well, it's not obvious to me, why couldn't a service decide to stop
running by itself?
At any rate I think the point is clear: a .NET service cannot easily stop
itself. We can agree to disagree about wether a C++ ATL service could.
Greetings,
Wessel
Sorry but I have to disagree.
First, <why couldn't a service decide to stop running by itself?>
When the SCM registers a service it creates a default security descriptor
for the Service Object, Local authenticated users like NETWORK SERVICE and
LOCAL SERVICE aren't granted SERVICE_START and SERVICE_STOP access rights.
That means if your service runs under one of these accounts (preferred and
strongly advisable), they aren't allowed to stop themselves or start/stop
any other service in the system (was the OP's issue right?). Services that
run as SYSTEM are granted SERVICE_STOP and SERVICE_START access rights
(amongst some other), local administrators and power users are granted all
access (SERVICE_ALL_ACCESS).
Note that above assumes you didn't modify the DACL for the service object
(something only administrator can do).
Or in short Service applications must conform to the interface rules of the
SCM, and this can only be guaranteed if both controller and service are
separate applications.
Second, <Every stopping service calls ExitProcess>.
Which is obviously not true, but ok, you relaxed this a bit:
< If there are multiple services in a process, the process would only call
ExitProcess when all of its services are stopped.>
But again, this is not how things works.
The ServiceControlDispatcher is started when the Service is started (by the
SCM SERVICE_START command), and it runs on a separate thread that doesn't
return until there is an error (like a rude kill) or all services in the
process have terminated accurately following the SCM interface SERVICE_STOP
protocol (see later). When all services in the process have terminated, the
SCM sends a control request to the dispatcher thread asking it to exit. When
the dispatcher thread terminates (that is when StartServiceCtrlDispatcher
returns) the process returns to the Run method class Dispose() on all
Service classes and finally exits Main() and the CLR handles the remaining
of the process shutdown. You see no ExitProcess called ever.
Ok, lets assume you want to call ExitProcess, but let's start with a
warning, never terminate a managed process using Win32 ExitProcess API, you
have to call Environment.Exit() so that the CLR can initiate an orderly
shutdown.
Now you can definitely call this method whenever you like in a service, but
you shouldn't, for the obvious reason that:
- You might disturb the SCM!
- you might have other services running in the process AND,
- your service might have dependent services running and these must be
stopped before your service stops.
The latter is taken care of by the ServiceController.Stop() [assumed you
call this from within your service, which you shouldn't do], but he! here
you have the same issue as explained above, the ServiceControler.Stop()
cannot stop dependencies from within the service unless it runs as
Administrator or localsystem.
The second one should be taken care of by you, that means that each service
needs to keep track of all services in the same process and only call
Environment.Exit() when all are stopped, not impossible to do but useless.
Why? well again the SCM didn't ask to stop, so you are not following the
interface rules imposed by the SCM, you probably didn't stop all running
threads, that might confuse the SCM and might result in a state where the
service cannot be 'stopped' or 'restarted'. If you follow the rules of the
SCM, and use the STOP protocol sequence (SERVICE_CONTROL_STOP/
SERVICE_STOPPED) the process will exit in an orderly fashion.
If the SERVICE_CONTROL_STOP request is not honored by a SERVICE_STOPPED, the
SCM will wait "WaitToKillServiceTimeout" registry value before terminating
the process.
Willy.