◆VB 2005でのWindows Service制御 - 実装2

[ 上へ ]

・スタートアップの種類

最初に書いた コントロールパネル管理ツールサービスからSQL Server 2005 Expressの

サービスの設定のうちスタートアップの種類のコンボボックスをプルダウンしてみると 種類としては「自動」、「手動」、「無効」があることがわかります。自動はPCを起動した際に 自動的にサービスを開始し、手動はユーザがサービスを開始しなければなりません。

無効に設定されていると、この設定を変えない限りサービスは開始できません。

この3つの設定値はSystem.ServiceProcess名前空間内に ServiceStartMode列挙定数 として定義されています。
オブジェクトブラウザ で確認してください。

ここには Automatic = 2Manual = 3Disable = 4の定数が定義されています。 定義されているということはどこかのクラスで使われているはずですからそれを利用したいのですが、 この定数を使っているクラスはServiceInstallerで、今回の処理とはちょっと関係ないので 利用できないです。(やろうと思えばできるかもしれませんが、継承関係がややこしくなるので 今回は使わないです)

サービスのインストール時にServiceInstaller.StartTypeでスタートアップの種類を設定して いますが、どのように設定しているのでしょう?

実はこの設定は レジストリ に書き込まれています。レジストリも範囲が広いですが、 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\サービス名
「Start」 に対応する値がそれにあたります。

早速、レジストリエディタでSQL Serverのサービスを 見てみます。スタート→ファイル名を指定して実行(R)からregedit.exeとタイプして実行します。 上記のパスで探すと以下のような画面があらわれます。

画面右のペインの「Start」に関連した値0x00000003がそれです。 この値はServiceStartModeの列挙体に対応しています。
ということで、スタートアップの種類を取得/変更するにはレジストリのこの値を参照/変更 すればよいことになります。

・VB 2005 での レジストリ処理

VB2005でレジストリを扱うのはVB6以前と異なり容易いです。ちゃんと対応したクラスが Microsoft.Win32名前空間にRegistryRegistryKeyとして用意されているのでこれを使います。 オブジェクトブラウザ で確認しておきましょう。

・スタートアップの種類の取得/変更の実装

まずは、Registry、RegistryKeyの2つのクラスを 扱いやすくするため名前空間をインポートします。
System.ServiceController名前空間をインポートした 次の行に以下のように記述します。

Imports System.ServiceProcess  'サービス関係      (この行は既にできています)

Imports Microsoft.Win32        'レジストリ

次にスタートアップの種類の設定値ですが、そのままServiceProcess名前空間のServiceStartModeを 使ってもいいのですが、サービスの状態の変更処理のときと同様に例外処理のために再定義 します。
前に定義したServiceControllerStatus列挙体の次あたりに以下のように定義してください。

'スタートアップの種類
Public Enum ServiceStartMode
    Automatic = 2
    Manual = 3
    Disable = 4
    None = 0        '例外処理用
End Enum

それでは中心になるスタートアップの変更/取得の処理をします。サービスの状態の変更のときのように 今回は「動作」というよりは「属性」なのでプロパティとして実装します。 プロパティ名はStartMode、戻値、設定値の型は上記で定義したServiceStartModeにして、 ChangeStatusメソッドの下に記述すると以下のようなスケルトンができます。

'スタートアップの種類の取得/変更
Public Property StartMode() As ServiceStartMode
    Get

    End Get
    Set(ByVal value As ServiceStartMode)

    End Set
End Property

まずはGet 〜 End Get内に値を取得する記述を以下のようにします。

Get
    Const SERVICEKEY As String = "SYSTEM\CurrentControlSet\Services\"          '(A)
    Dim regkey       As RegistryKey         '(B)
    Dim returnval    As ServiceStartMode    '(B)

    Try
        regkey = Registry.LocalMachine.OpenSubKey(SERVICEKEY + ServiceName())   '(C)

        Try
            returnval = CType(regkey.GetValue( "Start",
                                    ServiceStartMode.None), ServiceStartMode)   '(D)
        Catch ex As Exception
            returnval = ServiceStartMode.None
        Finally
            regkey.Close()
        End Try

    Catch ex As Exception
        Return ServiceStartMode.None
    End Try

    Return returnval
End Get

(A)は前述した サービスの設定が保持されているパスで固定なのでConstとします。

(B)でRegistryKey型とServiceStartMode型の変数を宣言します。

(C)でレジストリのSERVICEKY + ServiceName()で指定されるサービスの レジストリキーをオープンします。ServiceName()プロパティは基底クラスのプロパティで コンストラクタの引数で指定したサービス名を返します。ここで例外がスローされたら Catchブロックに制御が移りServiceStartMode.Noneを返して終了します。

(D)のGetValueメソッドで第一引数に指定した名前("Start")に 対応した値を取得して、CTypeの第二引数で指定した型(ServiceStartMode)に 変換してreturnvalに返します。
もし、"Start"が項目名としてないときや例外がスローされた時は、 GetValueメソッドの第二引数の値(ServiceStatrMode.None)を返し、 Finallyブロックでレジストリキーを閉じます。

次にSet 〜 End Set(値設定)を以下のように記述します。

Set(ByVal value As ServiceStartMode)
    Const SERVICEKEY As String = "SYSTEM\CurrentControlSet\Services\"
    Dim regkey       As RegistryKey

    Try
        regkey = Registry.LocalMachine.OpenSubKey(SERVICEKEY + ServiceName(), True) '(a)

        Try
            regkey.SetValue( "Start", value, RegistryValueKind.DWord)      '(b)
        Catch ex As Exception
            Return
        Finally
            regkey.Close()

        End Try

    Catch ex As Exception
        Return
    End Try
End Set

Property Getとの違いは(a)のOpenSubKeyメソッドで第二引数にTrueがあること、 (b)でGetValueからSetValueメソッドに変わったことです。OpenSubKeyはレジストリに書き込みを 許可するため、オーバーロード版のOpenSubKeyを使います。SetValueは値をレジストリに 書き込まないといけないのでわかりますよね。

以上ですが、終わる前にコンストラクタで サービス名だけを指定する バージョンだけ実装したのを覚えていますでしょうか。これはサービスを制御する対象を ローカルコンピュータに限るためでした。
ところが、基底クラスのServiceControllerにはMachineName()プロパティがあり、 ここでリモートコンピュータを指定できてしまいます。これを防ぐためにこのプロパティを シャドウして、さらにリードオンリーにしてしまいます。 StartModeプロパティの下にでも以下を記述してください。

Public Shadows ReadOnly Property MachineName() As String
    Get
        Return My.Computer.Name()
    End Get
End Property

以上でServiceControllerExクラスの実装はできましたので、Main関数で利用してみます。