何となく Blog by Jitta
Microsoft .NET 考

目次

Blog 利用状況
  • 投稿数 - 585
  • 記事 - 18
  • コメント - 2121
  • トラックバック - 181
ニュース
  • 検索エンジンで来られた方へ:
    お望みの情報は見つかりましたか? よろしければ、コメント欄にどのような情報を探していたのか、ご記入ください。
It's ME!
  • はなおか じった
  • 世界遺産の近くに住んでます。
  • Microsoft MVP for Visual Developer ASP/ASP.NET 10, 2004 - 9, 2009
サイト内検索
広告

記事カテゴリ

書庫

日記カテゴリ

ギャラリ

その他

わんくま同盟

同郷

 

サービスを再起動させてみます。唐突ですが、そういう必要が生じたので。

Visual Studio 2008 にて、C++ の Win32 コンソール アプリケーションです。PSDK API を使うので、C++。

説明するのが面倒なので、さくっと行きます。

サービスを制御するには、まず、サービス マネージャにアクセスします。このとき、いくつかの特権が必要です。今回は、考えないことにします。

「再起動」すなわち、「停止してから起動」というコマンドは、無いようです。そこで、「停止する」というコマンドと、「起動する」というコマンドを発行します。

コマンドを発行するには、OpenService 関数で、サービスのハンドラを捕まえ、ControlService 関数を使用します。起動は、StartService 関数です。

サービスは、Windows Service を作成したことがある方ならおわかりだと思いますが、起動指示から30秒以内に起動状態にならなければなりません。逆に言うと、起動を指示したからといってすぐに起動するわけではありません。停止も同じ。よって、停止を指示してから、完全に停止するまで待つ必要があります。また、起動も同じく、完全に起動するまで待ちます。待っている間、QueryServiceStatus 関数で現在のステータスを取ります。

  1. サービス マネージャにアクセスする。

  2. 指定のサービスにアクセスする。

  3. 停止信号を送る。

  4. 停止するまで待つ。

    1. 状態を見る。

    2. SERVICE_STOPPED でなければ1秒待つ。
      SERVICE_STOPPED なら戻る。

    3. 累積で30秒待っていれば、タイムアウトとする。
      そうでなければ繰り返す。

  5. 起動する。

  6. 起動するまで待つ。

    1. 状態を見る。

    2. SERVICE_RUNNING でなければ1秒待つ。
      SERVICE_RUNNING なら戻る。

    3. 累積で30秒待っていれば、タイムアウトとする。
      そうでなければ繰り返す。

そんなこんなで、次のようなコード。タイトルにカッコがついているのは、これで終われないから。

このコードでは、破棄しなければならないリソースを確保したら、次の関数を呼び出す様にしました。これにより、リソースを使うところ=次の関数内では、何らかのエラーが発生したら遠慮なしに return することができます。なぜこんな作りにしてあるかというと、本当は .c なコードで使っているから(涙)

で、こいつを元に、「C++(あるいはオブジェクト指向言語)って、C に比べてこんな利点があるんだよ。」ってことが説明できるかな、と。。。セッション資料作った方が面白いような気もしてきた。


// RestartService.cpp : コンソール アプリケーションのエントリ ポイントを定義します。
//

#include "stdafx.h"
#include <locale.h>

DWORD RestartService(LPCTSTR serviceName);
DWORD StopThenStartService(SC_HANDLE manager, LPCTSTR serviceName);
DWORD StopServiceWithWaiting(SC_HANDLE service);
DWORD StartServiceWithWaiting(SC_HANDLE service);
DWORD WaitServiceUntilStatus(SC_HANDLE service, DWORD state, int timeout = 30);

int _tmain(int argc, _TCHAR* argv[])
{
    _tsetlocale(LC_ALL, _T("japanese_japan"));
    if (argc < 2) {
        _putts(_T("引数で、サービス名を指定してください。\n"));
        return ERROR_BAD_ARGUMENTS;
    } else {
        _tprintf(_T("サービス \"%s\" の起動・停止結果 : %d\n")
            , argv[1]
            ,RestartService(argv[1]));
    }
    return 0;
}

// 指定されたサービスを再起動する。
// serviceName : サービス名
DWORD RestartService(LPCTSTR serviceName)
{
    if (serviceName == NULL) { return ERROR_BAD_ARGUMENTS; }

    SC_HANDLE manager;
    manager = OpenSCManager(NULL    /* ローカル コンピュータ */
        , NULL                        /* 規定のマネージャ */
        , SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE | GENERIC_EXECUTE
        );

    if (manager != NULL) {
        DWORD result;
        result = StopThenStartService(manager, serviceName);
        (void) CloseServiceHandle(manager);
        return result;

    } else {
        return GetLastError();
    }
}

// 指定されたサービスを停止させ、起動する。
// manager : サービス マネージャ
// serviceName : サービス名
DWORD StopThenStartService(SC_HANDLE manager, LPCTSTR serviceName)
{
    if (manager == NULL || serviceName == NULL) { return ERROR_BAD_ARGUMENTS; }

    SC_HANDLE    service;
    service = OpenService(manager, serviceName, GENERIC_EXECUTE | GENERIC_READ);

    if (service != NULL) {
        DWORD result;
        // サービスを停止する
        if ((result = StopServiceWithWaiting(service)) == ERROR_SUCCESS) {
            // サービスを起動する
            result = StartServiceWithWaiting(service);
        }
        (void) CloseServiceHandle(service);
        return result;

    } else {
        return GetLastError();
    }
}

// 指定のサービスを停止させ、停止するまで待つ。
// service : サービスのハンドラ
DWORD StopServiceWithWaiting(SC_HANDLE service)
{
    if (service == NULL) { return ERROR_BAD_ARGUMENTS; }

    SERVICE_STATUS status;
    BOOL ret = ControlService(service, SERVICE_CONTROL_STOP, &status);
    DWORD result = GetLastError();
    if (ret == TRUE || result == ERROR_SERVICE_NOT_ACTIVE) {
        result = WaitServiceUntilStatus(service, SERVICE_STOPPED);
    }
    return result;
}

// 指定のサービスを起動させ、起動するまで待つ。
// service : サービスのハンドラ
DWORD StartServiceWithWaiting(SC_HANDLE service)
{
    if (service == NULL) { return ERROR_BAD_ARGUMENTS; }

    BOOL ret = StartService(service, 0, NULL);
    DWORD result = GetLastError();
    if (ret == TRUE || result == ERROR_SERVICE_ALREADY_RUNNING) {
        result = WaitServiceUntilStatus(service, SERVICE_RUNNING);
    }
    return result;
}

// サービスが、指定の状態になるまで待つ。
// service : サービスのハンドラ
// state : 期待する状態
// timeout : 期待する状態になるまで待機する秒数(既定値 30)
DWORD WaitServiceUntilStatus(SC_HANDLE service, DWORD state, int timeout)
{
    DWORD result;
    SERVICE_STATUS status;
    int count = 0;
    while (TRUE) {
        if (QueryServiceStatus(service, &status)) {
            if (status.dwCurrentState == state) {
                result = ERROR_SUCCESS;
                break;
            }
            if (++count > timeout) {
                result = ERROR_SERVICE_REQUEST_TIMEOUT;
                break;
            }
        } else {
            result = GetLastError();
            break;
        }
        Sleep(1000);
    }
    return result;
}
投稿日時 : 2008年8月29日 22:55
コメント
  • # re: サービスを再起動する(フラット)
    渋木宏明(ひどり)
    Posted @ 2008/08/29 23:32
    >そういう必要

    コンソールアプリに仕上げるんなら、net コマンドでよかったのでわ。(サンプルコードだから?)
  • # re: サービスを再起動する(フラット)
    ちゃっぴ
    Posted @ 2008/08/30 0:04
    Windows Sewrvice 再起動するのに特権は必要ないですね。
    Service の ACL で許可されていればどの user からでも扱うこと出来るでしょう。
  • # re: サービスを再起動する(フラット)
    ちゃっぴ
    Posted @ 2008/08/30 0:06
    Windows Sewrvice 再起動するのに特権は必要ないですね。
    Service の ACL で許可されていればどの user からでも扱うこと出来るでしょう。

    > コンソールアプリに仕上げるんなら、net コマンドでよかったのでわ。

    やっぱり API 利用した方が良いのでそういう意味では WMI がお手軽だと思われます。
  • # re: サービスを再起動する(フラット)
    Jitta
    Posted @ 2008/08/30 8:49
    コメントありがとうございます。

    WMI って、Windows 2000 でも使えましたっけ?
    色々事情があって、もとのコードは VC6 だったりします。

    次のネタなのですが、net コマンドって、依存関係をたどってくれるのですか?!だったら、今からでも書き直しますっ!!
  • # re: サービスを再起動する(フラット)
    Jitta
    Posted @ 2008/08/30 8:52
    あ、ここに出したコードがコンソール アプリケーションなのは、説明用に実行できるように、ですよ。実際は、他のところで作っているアプリケーションから呼ばれるライブラリです。
  • # re: サービスを再起動する(フラット)
    ちゃっぴ
    Posted @ 2008/08/30 15:38
    > WMI って、Windows 2000 でも使えましたっけ?

    標準で使えます。
  • # re: サービスを再起動する(フラット)
    渋木宏明(ひどり)
    Posted @ 2008/08/30 18:01
    >やっぱり API 利用した方が良いのでそういう意味では WMI がお手軽だと思われます。

    net コマンドを Process.Start() せいと言っているわけではなく、コンソールコマンドを自作する必要があるの?てことです。

    >net コマンドって、依存関係をたどってくれるのですか?!

    見て、依存サービスがある場合は「いいの?」って訪ねてきたような気がします。
  • # re: サービスを再起動する(フラット)
    ちゃっぴ
    Posted @ 2008/08/30 20:31
    >net コマンドって、依存関係をたどってくれるのですか?!

    勝手に起動しますよ。WMI でも同じだったはずですけど。

    たとえば w3svc を起動するときは IISAdmin は勝手に起動します。
  • # re: サービスを再起動する(フラット)
    Jitta
    Posted @ 2008/08/31 7:09
    InstallShield のカスタム スクリプトなので、net コマンドは無理として、WMI は、抜け落ちてたねぇ(-_-;
  • # サービスを再起動する(問題発生編)
    何となく Blog by Jitta
    Posted @ 2008/09/03 21:50
    サービスを再起動する(問題発生編)
  • # サービスを再起動する(オブジェクト指向的修正編)
    何となく Blog by Jitta
    Posted @ 2008/09/30 22:04
    サービスを再起動する(オブジェクト指向的修正編)
タイトル  
名前  
Url
コメント