5

C ++からWindowsサービスにコマンドを送信するにはどうすればよいですか?同等の.NETコードは次のとおりです。

ServiceController sc = new ServiceController("MyService");
sc.ExecuteCommand(255);
4

3 に答える 3

3

ネイティブ C++ から、次のことを行う必要があります。

  1. サービス コントロール マネージャーへのハンドルを開き、
  2. サービス コントロール マネージャーを使用して、制御するサービスのサービス ハンドルを取得します。
  3. 制御コードまたはコードをサービスに送信し、
  4. 手順 1 と 2 で開いたハンドルを閉じます。

たとえば、次のコードは時刻同期サービスを再起動します。まず、サービス ハンドルのラッパー クラスを作成し、ブロックを離れるときに自動的に閉じるようにします。

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

次に、サービス コントロール マネージャー ( OpenSCManager()を使用) と、制御するサービスを開きます。OpenService()の dwDesiredAccess パラメータには、送信する各コントロールのアクセス許可を含める必要があります。そうしないと、関連するコントロール機能が失敗します。

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;

サービスを停止するには、ControlService()を使用して SERVICE_CONTROL_STOP コードを送信し、戻り値をチェックしてコマンドが成功したことを確認します。ERROR_SERVICE_NOT_ACTIVE 以外のエラーが報告された場合、サービスの開始は成功しないと思います。

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

サービスの停止を指示した後、サービス マネージャーが実際にサービスを停止したことを報告するのを待ちます。このコードには潜在的なバグが 2 つあります。製品コード用に修正することをお勧めします。

  1. Sleep(1000) はこのスレッドのメッセージ ループを中断するため、この関数が UI スレッドで実行される場合は、別の方法を使用して実行を遅らせる必要があります。MsgWaitForMultipleObjectsEx()を使用して、適切な sleep-with-message-loop を構築できます。
  2. GetTickCount ()から返された DWORD は最終的にゼロに戻ります。この関数が待機している間にラップアラウンドすると、意図したよりも早く待機が放棄される可能性があります。

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

最後に、サービスが停止状態にあることがわかっているので、StartService()を呼び出して再度実行します。

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

    return TRUE;
}
于 2009-10-20T13:24:28.367 に答える
3

ControlServiceを使用します。 Service Control Requestsを参照してください。

于 2009-10-20T07:15:58.770 に答える