Frage

Wie kann ich den Befehl an einen Windows -Dienst von C ++ senden? Der äquivalente .NET -Code ist:

ServiceController sc = new ServiceController("MyService");
sc.ExecuteCommand(255);
War es hilfreich?

Lösung

Aus nativem C ++ müssen Sie:

  1. Öffnen Sie einen Handle für den Service Control Manager,
  2. Verwenden Sie den Service Control Manager, um einen Service -Handle für den Service zu erhalten, den Sie steuern möchten.
  3. Senden Sie einen Kontrollcode oder Codes an den Dienst, und
  4. Schließen Sie die in den Schritten 1 und 2 geöffneten Griffen.

Dieser Code startet beispielsweise den Zeitsynchronisierungsdienst neu. Zunächst erstelle ich eine Wrapper -Klasse für die Service -Handles, um sie automatisch zu schließen, wenn ich den Block verließ.

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

Dann öffne ich den Service Control Manager (mithilfe OpenScmanager ()) und der Service, den ich kontrollieren möchte. Beachten OpenService () Muss Berechtigungen für jede Kontrolle enthalten, die ich senden möchte, oder die entsprechenden Kontrollfunktionen scheitern.

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;

Um den Service zu stoppen, benutze ich ControlService () So senden Sie den Code service_control_stop und überprüfen Sie den Rückgabewert, um sicherzustellen, dass der Befehl erfolgreich ist. Wenn ein anderer Fehler als error_service_not_active gemeldet wird, gehe ich davon aus, dass der Start des Dienstes nicht erfolgreich sein wird.

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

Nachdem ich den Service angewiesen habe, einzustellen, warte ich darauf, dass der Service Manager berichtet, dass der Service tatsächlich gestoppt wird. Dieser Code verfügt über zwei potenzielle Fehler, die Sie möglicherweise für den Produktionscode korrigieren möchten:

  1. Sleep (1000) wird die Nachrichtenschleife in diesem Thread aussetzen. Sie sollten daher eine andere Methode verwenden, um die Ausführung zu verzögern, wenn diese Funktion auf einem UI -Thread ausgeführt wird. Sie können einen geeigneten Schlaf mit Message-Schleife verwenden MSGWAITFORMULTIPLIPIPIPIPSOWSEX ().
  2. Das DWORD wurde von zurückgegeben Gettickcount () wird irgendwann auf Null umwickeln; Wenn es sich um die Wartezeit dieser Funktion handelt, kann das Warten früher aufgeben, als ich beabsichtigt habe.

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

Als ich weiß, dass der Dienst in einem gestoppten Zustand ist, rufe ich an, rufe ich an Dienst starten() Führen Sie es noch einmal aus.

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

    return TRUE;
}

Andere Tipps

Hier ist ein kleines Programm, das eine Verbindung zu einem Dienst namens "MyService" herstellt und dann einen Befehl 141 sendet (der vom Dienst definiert wird)

// 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;
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top