Send command to service from C++
Asked Answered
D

3

6

how can I send command to a Windows service from C++? Equivalent .NET code is:

ServiceController sc = new ServiceController("MyService");
sc.ExecuteCommand(255);
Doralin answered 20/10, 2009 at 7:7 Comment(0)
T
3

From native C++, you will need to:

  1. Open a handle to the service control manager,
  2. Use the service control manager to obtain a service handle for the service you want to control,
  3. Send a control code or codes to the service, and
  4. Close the handles opened in steps 1 and 2.

For example, this code restarts the time synchronization service. First, I create a wrapper class for the service handles, to close them automatically when leaving the block.

class CSC_HANDLE
{
public:
 CSC_HANDLE(SC_HANDLE h) : m_h(h) { }
 ~CSC_HANDLE() { ::CloseServiceHandle(m_h); }
 operator SC_HANDLE () { return m_h; }
private:
 SC_HANDLE m_h;
};

Then, I open the service control manager (using OpenSCManager()) and the service I want to control. Note that the dwDesiredAccess parameter to OpenService() must include permissions for each control I want to send, or the relevant control functions will fail.

BOOL RestartTimeService()
{
    CSC_HANDLE hSCM(::OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, GENERIC_READ));
    if (NULL == hSCM) return FALSE;

    CSC_HANDLE hW32Time(::OpenService(hSCM, L"W32Time", SERVICE_START | SERVICE_STOP | SERVICE_QUERY_STATUS));
    if (NULL == hW32Time) return FALSE;

To stop the service, I use ControlService() to send the SERVICE_CONTROL_STOP code, and then check the return value to make sure the command succeeded. If any error other than ERROR_SERVICE_NOT_ACTIVE is reported, I assume that starting the service is not going to succeed.

    SERVICE_STATUS ss = { 0 };
    ::SetLastError(0);
    BOOL success = ::ControlService(hW32Time, SERVICE_CONTROL_STOP, &ss);
    if (!success)
    {
        DWORD le = ::GetLastError();
        switch (le)
        {
        case ERROR_ACCESS_DENIED:
        case ERROR_DEPENDENT_SERVICES_RUNNING:
        case ERROR_INVALID_HANDLE:
        case ERROR_INVALID_PARAMETER:
        case ERROR_INVALID_SERVICE_CONTROL:
        case ERROR_SERVICE_CANNOT_ACCEPT_CTRL:
        case ERROR_SERVICE_REQUEST_TIMEOUT:
        case ERROR_SHUTDOWN_IN_PROGRESS:
            return FALSE;

        case ERROR_SERVICE_NOT_ACTIVE:
        default:
            break;
        }
    }

After instructing the service to stop, I wait for the service manager to report that the service is in fact stopped. This code has two potential bugs, which you may wish to correct for production code:

  1. Sleep(1000) will suspend the message loop on this thread, so you should use another method to delay execution if this function will run on a UI thread. You can construct a suitable sleep-with-message-loop using MsgWaitForMultipleObjectsEx().
  2. The DWORD returned from GetTickCount() will wrap around to zero eventually; if it wraps around while this function is waiting, the wait may give up sooner than I intended.

    DWORD waitstart(::GetTickCount());
    while (true)
    {
        ZeroMemory(&ss, sizeof(ss));
        ::QueryServiceStatus(hW32Time, &ss);
        if (SERVICE_STOPPED == ss.dwCurrentState) break;
        ::Sleep(1000);
        DWORD tick(::GetTickCount());
        if ((tick < waitstart) || (tick > (waitstart + 30000))) return FALSE;
    }
    

Finally, knowing that the service is in a stopped state, I call StartService() run it again.

    success = ::StartService(hW32Time, 0, NULL);
    if (!success) return FALSE;

    return TRUE;
}
Teenateenage answered 20/10, 2009 at 13:24 Comment(0)
P
3

You use ControlService, see Service Control Requests.

Palladin answered 20/10, 2009 at 7:15 Comment(0)
T
3

From native C++, you will need to:

  1. Open a handle to the service control manager,
  2. Use the service control manager to obtain a service handle for the service you want to control,
  3. Send a control code or codes to the service, and
  4. Close the handles opened in steps 1 and 2.

For example, this code restarts the time synchronization service. First, I create a wrapper class for the service handles, to close them automatically when leaving the block.

class CSC_HANDLE
{
public:
 CSC_HANDLE(SC_HANDLE h) : m_h(h) { }
 ~CSC_HANDLE() { ::CloseServiceHandle(m_h); }
 operator SC_HANDLE () { return m_h; }
private:
 SC_HANDLE m_h;
};

Then, I open the service control manager (using OpenSCManager()) and the service I want to control. Note that the dwDesiredAccess parameter to OpenService() must include permissions for each control I want to send, or the relevant control functions will fail.

BOOL RestartTimeService()
{
    CSC_HANDLE hSCM(::OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, GENERIC_READ));
    if (NULL == hSCM) return FALSE;

    CSC_HANDLE hW32Time(::OpenService(hSCM, L"W32Time", SERVICE_START | SERVICE_STOP | SERVICE_QUERY_STATUS));
    if (NULL == hW32Time) return FALSE;

To stop the service, I use ControlService() to send the SERVICE_CONTROL_STOP code, and then check the return value to make sure the command succeeded. If any error other than ERROR_SERVICE_NOT_ACTIVE is reported, I assume that starting the service is not going to succeed.

    SERVICE_STATUS ss = { 0 };
    ::SetLastError(0);
    BOOL success = ::ControlService(hW32Time, SERVICE_CONTROL_STOP, &ss);
    if (!success)
    {
        DWORD le = ::GetLastError();
        switch (le)
        {
        case ERROR_ACCESS_DENIED:
        case ERROR_DEPENDENT_SERVICES_RUNNING:
        case ERROR_INVALID_HANDLE:
        case ERROR_INVALID_PARAMETER:
        case ERROR_INVALID_SERVICE_CONTROL:
        case ERROR_SERVICE_CANNOT_ACCEPT_CTRL:
        case ERROR_SERVICE_REQUEST_TIMEOUT:
        case ERROR_SHUTDOWN_IN_PROGRESS:
            return FALSE;

        case ERROR_SERVICE_NOT_ACTIVE:
        default:
            break;
        }
    }

After instructing the service to stop, I wait for the service manager to report that the service is in fact stopped. This code has two potential bugs, which you may wish to correct for production code:

  1. Sleep(1000) will suspend the message loop on this thread, so you should use another method to delay execution if this function will run on a UI thread. You can construct a suitable sleep-with-message-loop using MsgWaitForMultipleObjectsEx().
  2. The DWORD returned from GetTickCount() will wrap around to zero eventually; if it wraps around while this function is waiting, the wait may give up sooner than I intended.

    DWORD waitstart(::GetTickCount());
    while (true)
    {
        ZeroMemory(&ss, sizeof(ss));
        ::QueryServiceStatus(hW32Time, &ss);
        if (SERVICE_STOPPED == ss.dwCurrentState) break;
        ::Sleep(1000);
        DWORD tick(::GetTickCount());
        if ((tick < waitstart) || (tick > (waitstart + 30000))) return FALSE;
    }
    

Finally, knowing that the service is in a stopped state, I call StartService() run it again.

    success = ::StartService(hW32Time, 0, NULL);
    if (!success) return FALSE;

    return TRUE;
}
Teenateenage answered 20/10, 2009 at 13:24 Comment(0)
D
1

Here is a little program which will connect to a service called "MYSERVICE" then send a command 141 (which is defined by the service)

// ServiceCommunicator.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <Windows.h>
#include <iostream>

using namespace std;

int main()
{
    SC_HANDLE managerHandle;
    SC_HANDLE serviceHandle;

    SERVICE_STATUS   controlParms;
    DWORD retStatus;

    managerHandle = OpenSCManager(NULL, NULL, GENERIC_READ);
    if (NULL != managerHandle)
    {
        serviceHandle = OpenService(managerHandle, L"MYSERVICE", SERVICE_USER_DEFINED_CONTROL | SERVICE_QUERY_STATUS);

        if (NULL != serviceHandle)
        {
            cout << "connected to Service" << endl;
            retStatus = ControlService(serviceHandle, 141, &controlParms);

            if (retStatus)
            {
                //Get the return code from the service
                cout << "For command 141, return code from service was " << controlParms.dwWin32ExitCode << endl;
            }
            else
                cout << "Sending command 141 failed" << endl;

            CloseServiceHandle(serviceHandle);
        }
        else
        {
            cout << "could not connect to Service" << endl;
        }

        CloseServiceHandle(managerHandle);
    }
    else
    {
        cout << "could not open service manager" << endl;
    }
    return 0;
}
Donielle answered 27/4, 2018 at 21:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.