Domanda

Come posso inviare un comando a un servizio di Windows da C ++? codice equivalente NET è:

ServiceController sc = new ServiceController("MyService");
sc.ExecuteCommand(255);
È stato utile?

Soluzione

Da nativo C ++, è necessario:

  1. Aprire una maniglia al gestore di controllo del servizio,
  2. Utilizzare il gestore di controllo del servizio per ottenere un handle servizio per il servizio che si desidera controllare,
  3. Invia un codice di controllo o di codici per il servizio, e
  4. Chiudere le maniglie aperte ai punti 1 e 2.

Ad esempio, questo codice viene riavviato il servizio di sincronizzazione tempo. In primo luogo, creo una classe wrapper per le maniglie di servizio, di chiudere automaticamente al momento di lasciare il blocco.

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;
};

Poi, apro la Gestione controllo servizi (utilizzando OpenSCManager () ) e il servizio che voglio controllare. Si noti che il parametro dwDesiredAccess a OpenService () deve includere i permessi per ogni controllo voglio inviare, o le funzioni di controllo competenti non riuscirà.

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;

Per arrestare il servizio, io uso ControlService () per inviare il codice SERVICE_CONTROL_STOP, e quindi controllare il valore di ritorno per assicurarsi che il comando è riuscito. Se viene segnalato alcun errore diverso da ERROR_SERVICE_NOT_ACTIVE, presumo che l'avvio del servizio non sta per avere successo.

    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;
        }
    }

Dopo istruire il servizio di smettere, io aspetto il responsabile del servizio di segnalare che il servizio è di fatto interrotto. Questo codice ha due potenziali bug, che si potrebbe desiderare di correggere il codice di produzione:

  1. Il sonno (1000) sospende il ciclo di messaggi su questo thread, così si dovrebbe utilizzare un altro metodo per ritardare l'esecuzione se questa funzione verrà eseguito su un thread dell'interfaccia utente. È possibile costruire un adeguato sonno-con-messaggio-loop utilizzando MsgWaitForMultipleObjectsEx () .
  2. Il DWORD tornato da GetTickCount () sarà avvolgere intorno a zero alla fine; se si avvolge intorno mentre questa funzione è in attesa, l'attesa può rinunciare prima di quanto ho inteso.

    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;
    }
    

Infine, sapendo che il servizio è in uno stato di interruzione, che io chiamo StartService () eseguirlo nuovamente.

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

    return TRUE;
}

Altri suggerimenti

Ecco un piccolo programma che si collegherà a un servizio chiamato "MYSERVICE" quindi inviare un comando 141 (che è definito dal servizio)

// 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;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top