kokbira
kokbira

Reputation: 608

How to control Windows services using Windows API easily?

Issue

I would like to control any Windows service using Windows API, but I cannot :(

= What's wrong with my "wanting solution"?

Current solution

The ugliest but functional way I use is following (for Stop command):

void StopService()
{
    ShellExecute(Application->Handle,
                 "open",
                 "stop.bat",
                 NULL,
                 ExtractFilePath(Application->ExeName).c_str(),
                 SW_SHOW);
}

Where stop.bat is a Windows batch file with following commands:

::the command
sc stop "service name"
::a pause to see if something goes wrong
pause

It is very annoying to have to close launched cmd windows...

Wanting solution

The function bellow appears to be ok. If I try to perform a command to stop an unexisting service, it will show a error message. BUT when I try to run it for an existing service, it runs, but nothing happens with the service (if I ordain to stop it, it does nothing)...

void ControlService(AnsiString ServiceName, DWORD Order)
{
    SC_HANDLE Scm, Svc;
    SERVICE_STATUS Status;

    Scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);

    if(Scm != 0)
    {
        Svc = OpenService(Scm, PChar(ServiceName.c_str()), SERVICE_ALL_ACCESS);

        if(Svc != 0)
        {
            ControlService(Svc, Order, &Status);

            // handle Status....
            CloseServiceHandle(Svc);
        }
        else
        {
            ShowLastError();
        }
        CloseServiceHandle(Scm);
    }
    else
    {
        ShowLastError();
    }
}

void ShowLastError()
{
    LPVOID lpMsgBuf;

    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
        NULL,
        GetLastError(),
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
        (LPTSTR) &lpMsgBuf,
        0,
        NULL
    );

    // Display the string.
    MessageBox(NULL, (char*)lpMsgBuf, "GetLastError", MB_OK|MB_ICONINFORMATION);

    // Free the buffer.
    LocalFree(lpMsgBuf);
}

What's wrong?????

Upvotes: 4

Views: 2153

Answers (1)

seva titov
seva titov

Reputation: 11910

Two possible issues you might have run into:

  1. You are checking state of service too soon. The ServiceControl returns immediately, without waiting for the service to complete the request. Your service could transition to SERVICE_STOP_PENDING, and in this case you will have to give it some time and check again.
  2. There are dependent services in running state. You will have to enumerate dependent services, shut them down first, then stop your service.

On #2, there is good explanation with a sample code on MSDN. Here is a quote from the same page:

However, if the SCM determines that other running services are dependent on the specified service, it will not forward the stop request. Instead, it returns ERROR_DEPENDENT_SERVICES_RUNNING. Therefore, to programmatically stop such a service, you must first enumerate and stop its dependent services.

Upvotes: 4

Related Questions