views:

929

answers:

2

I'm writing a service application that sometimes cannot be stopped immediately upon receiving the SERVICE_CONTROL_STOP from the Services MMC. I currently handle it like this: (in pseudo-code):

DWORD 
HandlerEx(
    DWORD dwControl, 
    DWORD dwEventType, 
    PVOID pvEventData, 
    PVOID pvContext
) 
{
    switch( dwControl )
    {
    case SERVICE_CONTROL_STOP:
        if ( CanStopServiceNow() )
        {
             ReportStatus( SERVICE_STOP_PENDING );
             DoStopService();
             ReportStatus( SERVICE_STOPPED );
             return NO_ERROR;
        }
        else
             return ERROR_BUSY;
        break;
    }

    return NO_ERROR;
}

It works well when the service can stop ( and it does stop), but when it cannot stop, I would expect Windows to report the error I'm returning (ERROR_BUSY in this example.) Instead, Windows displays a message saying:

"The service did not return an error. 
This could be an internal Windows error or an internal service error."

So my question is, how to make the service report back to SCM that it cannot stop now and make MMC display a message about the actual error code that I'm returning?

+1  A: 

I am aware that this won't answer your specific question. But as I think that the answer is "There is no way". I will make a suggestion anyway. Most applications that need more control on their startup and shutdown, offhand I can think of Sap and Oracle, use the windows service as administrative service, and implement a gui or command line interface to start and stop the actual "worker process".

Igal Serban
That's exactly what I'm doing. But that does not answer my question, sorry.
Andrei Belogortseff
+1  A: 

Answering my own question (after investigating the problem in more detail): instead of returning an error code from the HandlerEx function, the service should report its status to SCM indicating whether or not it can accept the SERVICE_CONTROL_STOP code. If the service detects that it cannot be stopped immediately, it should clear the SERVICE_ACCEPT_STOP bit in its SERVICE_STATUS structure and call SetServiceStatus(). This should prevent SCM from attempting to stop the service in the first place. When the condition clears and the service decides it's safe to stop, it should set the SERVICE_ACCEPT_STOP bit and call SetServiceStatus() again.

Andrei Belogortseff