Question

comment puis-je envoyer la commande à un service Windows à partir de C ++? code .NET équivalent est:

ServiceController sc = new ServiceController("MyService");
sc.ExecuteCommand(255);
Était-ce utile?

La solution

De C ++ natif, vous devez:

  1. Ouvrez une poignée au gestionnaire de contrôle de service,
  2. Utilisez le gestionnaire de contrôle de service pour obtenir une poignée de service pour le service que vous souhaitez contrôler,
  3. Envoyer un code de contrôle ou codes au service et
  4. Fermer les poignées ouvertes dans les étapes 1 et 2.

Par exemple, ce code redémarre le service de synchronisation de temps. Tout d'abord, je crée une classe wrapper pour les poignées de service, pour les fermer automatiquement lorsque vous quittez le bloc.

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

Alors, j'ouvre le gestionnaire de contrôle de service (en utilisant OpenSCManager () ) et le service que je veux contrôler. Notez que le paramètre dwDesiredAccess OpenService () doit inclure des autorisations pour chaque contrôle que je veux envoyer, ou les fonctions de contrôle pertinentes échouera.

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;

Pour arrêter le service, j'utilise ControlService () pour envoyer le code SERVICE_CONTROL_STOP, puis vérifiez la valeur de retour pour vous assurer que la commande a réussi. Si une erreur autre que ERROR_SERVICE_NOT_ACTIVE est rapporté, je suppose que le démarrage du service ne va pas réussir.

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

Après avoir demandé au service d'arrêter, j'attends le gestionnaire de services pour signaler que le service est en fait arrêté. Ce code a deux bugs potentiels, que vous voudrez peut-être corriger le code de production:

  1. Sommeil (1000) suspend la boucle de message sur ce fil, vous devez donc utiliser une autre méthode pour retarder l'exécution si cette fonction fonctionnera sur un thread d'interface utilisateur. Vous pouvez construire un sommeil approprié avec message en boucle en utilisant MsgWaitForMultipleObjectsEx () .
  2. Le retour de DWORD GetTickCount () s'enrouler autour de zéro par la suite; si elle s'enroule autour lorsque cette fonction est en attente, l'attente peut renoncer plus tôt que je voulais.

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

Enfin, sachant que le service est dans un état arrêté, je l'appelle StartService () exécuter à nouveau.

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

    return TRUE;
}

Autres conseils

Voici un petit programme qui se connecte à un service appelé « MyService », puis envoyer une commande 141 (qui est définie par le 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;
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top