何となく Blog by Jitta
Microsoft .NET 考

目次

Blog 利用状況
  • 投稿数 - 761
  • 記事 - 18
  • コメント - 37042
  • トラックバック - 222
ニュース
  • IE7以前では、表示がおかしい。div の解釈に問題があるようだ。
    IE8の場合は、「互換」表示を OFF にしてください。
  • 検索エンジンで来られた方へ:
    お望みの情報は見つかりましたか? よろしければ、コメント欄にどのような情報を探していたのか、ご記入ください。
It's ME!
  • はなおか じった
  • 世界遺産の近くに住んでます。
  • Microsoft MVP for Visual Developer ASP/ASP.NET 10, 2004 - 9, 2011
広告

記事カテゴリ

書庫

日記カテゴリ

ギャラリ

その他

わんくま同盟

同郷

 

前回発生した問題に対して、オブジェクト指向で設計、実装されていたら、どうだったかを検証します。ここで考えるのは、前々回の段階です。

その前に、「オブジェクト指向」って、難しいのでしょうか?ということを考えます。「難しいのでしょうか?」というのは、正しくありません。「何が、難しいのでしょうか?」を考えます。

普通、人は、何かしなければならないとき、「どうする」か、考えます。あるいは、達成した状態になるために、どのようなことが必要か考えます。このとき考えることは、「行為」のみではないでしょうか。

とっても簡単な例では、「彼に、醤油をとってもらいたい」とします。私は、「醤油をとって」と頼みます。このように、「彼に頼む」という行為をすればよい、と考えるわけです。

ところが、オブジェクト指向では、「誰にさせるか」ということが重要になります。「私」にも「彼」にも、「ものを運ぶ」という行為はできます。したがって、「私」が「ものを運ぶ」のか、「彼」が「ものを運ぶ」のか、考えなければなりません。そして、「彼」に仕事をするために、「私」ができることは何か?と、考えます。そうすると、「彼に頼む」とだけ考えればよかった手続き指向に対して、オブジェクト指向では「誰が、彼に頼むのか」を考えなければならない、、、と、言えなくもありません。確かに面倒ではありそうです。つまり、手続き指向では「私が」という暗黙の了解があったわけです。ところが、オブジェクト指向では「私」はプログラムから切り離され、独立してしまうのです。そして「私」は、プログラム内の様々な「誰か」に対して、「他の人に頼む」という行為をさせることになります。そして、頼むためには、その「誰か」が、対象のことを「できる」かどうか、確認しておかなければならない、ということでもあります。

.NET Framework には、System.Windows.Forms.Form というクラスがあります。この「クラス」が「誰か」です。Form クラスには、Show というメソッドがあります。つまり、Form クラスは現れる(Show)ことができます。しかし、System.String クラスには Show メソッドがありません。したがって、String クラスは現れることができません。

余談:この原則を壊すのが、拡張メソッドですね。後付のような感じで「~することができる」を増やすことができます。

閑話休題。このように、オブジェクト指向では、「誰」ということ、その誰かが「何ができるか」について、注意を払わなければなりません。また、「誰か」つまりクラスを設計する場合は、そのクラスに「何をさせるべきか」について、注意を払わなければなりません。

オブジェクト指向が難しいのは、この、いわば「キャスティング」ではないでしょうか。ひとつのアプリケーションという演劇に、どのような俳優すなわちクラスを起用するのか。俳優によって得意とすることが異なります。同じように、クラスによってできること、得意なことが異なります。場合によっては、というかほとんどの場合、新しい俳優を捜さなければならないでしょう。すなわち、クラスの設計です。

では、キャスティング...というか、すでに潜んでいる俳優たちを見つけ出します。

まず、「停止させられ」たり、「起動させられ」たりする、「サービス」です。他者から「~させられる」ということは、自立して「~できる」ということです。注:国語的な意味ではなくて。プログラムの世界では、他からメッセージによって指示を与える、っていうのは、常識でしょ?

それから、サービス マネージャも、キャストとして(こう書くと、投げる、型変換する、みたいだけど違うよ)あげておきましょう。

こんなモンですかね。では、彼らは、どんなことができるのか、考えます。

サービス マネージャは、「アクセスする(される)」ことと、「アクセスを切り離す(切り離される)」ことができます。.NET Framework ではこれは「アンマネージド リソース」として扱うのですが、今回は Unmanaged C++ を考えているので、とりあえず、無視。他に、「ハンドル」というプロパティが必要ですね。これは、アクセスすることで得られるものなので、「読み取り専用」とします。

サービスは、「停止する(停止させられる)」ことと「起動する(起動させられる)」ことができます。サービスにもハンドルが必要ですが、これは、今回の用途には必要がないので、private にしておきます(アクセッサーを用意しない)。ハンドルがあるということは、「アクセスする」と「切り離す」もありますね。そして、起動や停止が完了するのを、「どれくらい待つか」というのを、指定できるようにしておきましょう。ただし、デフォルトを30秒として、また、30秒未満には設定できないようにします。

// サービス マネージャ ヘッダ
#pragma once

class ServiceManager
{
private:
    SC_HANDLE handle;

public:
    ServiceManager(void);
    virtual ~ServiceManager(void);
    DWORD Open(DWORD desiredAccess);
    void Close(void);
    const SC_HANDLE get_Handle(void);
};
// サービス マネージャ
#include "ServiceManager.h"

ServiceManager::ServiceManager(void)
{
    handle = NULL;
}

ServiceManager::~ServiceManager(void)
{
    if (handle != NULL) {
        Close();
    }
}

DWORD ServiceManager::Open(DWORD desiredAccess)
{
    if (handle == NULL) {
        handle = OpenSCManager(NULL, NULL, desiredAccess);
        if (handle == NULL) {
            DWORD ret = GetLastError();
            SetLastError(ret);
            return ret;
        }
    }
    SetLastError(ERROR_SUCCESS);
    return ERROR_SUCCESS;
}

void ServiceManager::Close(void)
{
    CloseServiceHandle(handle);
    handle = NULL;
}

const SC_HANDLE ServiceManager::get_Handle(void)
{
    return handle;
}
// サービス ヘッダ
#pragma once

class LocalService
{
private:
    SC_HANDLE handle;
    static unsigned int FixTimeout(unsigned int timeout) {
        // 30以上60未満に整形
        return (timeout < 30 ? 30 : (timeout > 60 ? 60 : timeout));
    };
    DWORD WaitForState(DWORD status, unsigned int timeout);

public:
    LocalService(void);
    virtual ~LocalService(void);
    DWORD Open(SC_HANDLE manager, LPCTSTR serviceName, DWORD desiredAccess);
    void Close(void);
    DWORD Start(unsigned int timeout = 30);
    DWORD Stop(unsigned int timeout = 30);
};
// サービス
#include "LocalService.h"

LocalService::LocalService(void)
{
    handle = NULL;
}

LocalService::~LocalService(void)
{
    if (handle != NULL) {
        Close();
    }
}

DWORD LocalService::Open(SC_HANDLE manager, LPCTSTR serviceName, DWORD desiredAccess)
{
    if (serviceName == NULL || manager == NULL || serviceName[0] == 0) {
        SetLastError(ERROR_BAD_ARGUMENTS);
        return ERROR_BAD_ARGUMENTS;
    }
    if (handle != NULL) {
        Close();
    }

    handle = OpenService(manager, serviceName, desiredAccess);
    if (handle == NULL) {
        DWORD result = GetLastError();
        SetLastError(result);
        return result;
    }
    SetLastError(ERROR_SUCCESS);
    return ERROR_SUCCESS;
}

void LocalService::Close(void)
{
    (void) CloseServiceHandle(handle);
    handle = NULL;
}

DWORD LocalService::Start(unsigned int timeout)
{
    if (handle == NULL) {
        return ERROR_NOT_READY;
    }
    timeout = FixTimeout(timeout);

    BOOL ret = StartService(handle, 0, NULL);
    DWORD result = GetLastError();
    if (ret == TRUE || result == ERROR_SERVICE_ALREADY_RUNNING) {
        result = WaitForState(SERVICE_RUNNING, timeout);
    }
    SetLastError(result);
    return result;
}

DWORD LocalService::Stop(unsigned int timeout)
{
    if (handle == NULL) {
        return ERROR_NOT_READY;
    }
    timeout = FixTimeout(timeout);

    SERVICE_STATUS status;
    BOOL ret = ControlService(handle, SERVICE_CONTROL_STOP, &status);
    DWORD result = GetLastError();
    if (ret == TRUE || result == ERROR_SERVICE_NOT_ACTIVE) {
        result = WaitForState(SERVICE_STOPPED, timeout);
    }
    SetLastError(result);
    return result;
}

DWORD LocalService::WaitForState(DWORD waitFor, unsigned int timeout)
{
    DWORD result;
    SERVICE_STATUS status;
    int count = 0;
    while (TRUE) {
        if (QueryServiceStatus(handle, &status)) {
            if (status.dwCurrentState == waitFor) {
                result = ERROR_SUCCESS;
                break;
            }
            if (++count > timeout) {
                result = ERROR_SERVICE_REQUEST_TIMEOUT;
                break;
            }
        } else {
            result = GetLastError();
            break;
        }
        Sleep(1000);
    }
    return result;
}
投稿日時 : 2008年9月17日 22:43
コメント
  • # サービスを再起動する(オブジェクト指向的修正編)
    何となく Blog by Jitta
    Posted @ 2008/09/30 22:04
    サービスを再起動する(オブジェクト指向的修正編)
  • # サービスを再起動する(オブジェクト指向的修正編)
    何となく Blog by Jitta
    Posted @ 2010/06/13 23:41
    サービスを再起動する(オブジェクト指向的修正編)
タイトル
名前
Url
コメント