・定数定義
前置きが長くなりましたがServiceControllerExの実装です。基本クラスの 継承の前に、System.ServiceProcess名前空間をインポートします。 ServiceControllerEx.vbの先頭にImports System.ServiceProcessを記述します。
そして、ServiceControllerを継承するためにServiceControllerExクラスの 一行目にInherits ServiceControllerをいれてください。
Imports System.ServiceProcess 'サービス関係名前空間
Public Class ServiceControllerEx
Inherits ServiceController
End Class
|
サービスをどの状態に変更したいのかを列挙定数として定義します。当然ですが、 サービスにはどのような状態があるのか分からないと定義できないです。 サービスの状態を取得するにはServiceController.Status()プロパティを使いますが、 この戻値がServiceControllerStatus列挙定数です。
実際どんな値があるのか見てみます。 ツールバーの表示(V)→オブジェクトブラウザ(J)からServiceControlStatus列挙定数を 選択すると画面右に定数値が表示されます。
これを見ると状態としては、Paused(一時停止)、Running(実行中)、 Stopped(停止)があることがわかります。ということは状態を変更するパターンとしては
Paused→Continue
Running→Paused
Running→Stopped
Stopped→Running
なので
Imports ステートメントの下あたりに以下のように列挙定数ServiceActionを定義してください。
Imports System.ServiceProcess 'サービス関係 'サービスステータス変更アクション Public Enum ServiceAction Svc_Start = 1 Svc_Stop = 2 Svc_Pause = 3 Svc_Continue = 4 End Enum Public Class ServiceControllerEx End Class |
次に、サービスの状態はStatus()で取得しますが存在しないサービス名で ServiceControllerのインスタンスを作ってStatus()プロパティを実行すると例外が 発生するので、その対策用にあらためてServiceControlStatus列挙定数を定義します。 ServiceAction列挙定数の定義の下に
'サービスステータス Public Enum ServiceControllerStatus Stopped = 1 StartPending = 2 StopPending = 3 Running = 4 ContinuePending = 5 PausePending = 6 Paused = 7 None = 0 '例外発生対策用 End Enum |
上記のように定義してください。None定数以外はSystem.ServiceProcess名前空間の ServiceConrollerStatusとまったく同じにしてください。
・コンストラクタの実装
最初にコンストラクタからです。基底クラスのServiceControllerは3つのコンストラクタがオーバー
ロードされています。今回はサービスを制御する対象のコンピュータはローカルに限っているので、
サービス名だけを引数に指定する、
ServiceController.New(name As String)だけを使います。
Public Class ServiceControllerExとEnd Classの間に以下を実装してください。
'コンストラクタ Public Sub New(ByVal name As String) MyBase.New(name) '基底クラスのコンストラクタ End Sub |
・Statusプロパティの実装
各メンバの実装です。まずは現在のサービスの状態を取得するStatus()プロパティからです。
基本クラスのStatus()となんらかわりないので実装する必要はないのですが、例外に
対応するためにシャドウします。
コンストラクタに続いて以下を記述してください。
宣言部のShadowsキーワードは基底クラスのServiceController.Status() プロパティを再定義するために、ReadOnlyは読み取り専用であることを示します。 Shadowsと似たものにオーバーライドがありますが、違いについては別の機会に書きたいと思います。 基本的な実装部分はMyBase.Status()だけです。例外がスローされたときはCatchステートメントで 先に定義した ServiceControllerStatus.Noneが返されるようにしています。
・ChangeStatusメソッドの実装
サービスの状態を変更するには基底クラスのContinue()、Pause()、Start()、Stop()を実行することに なります。ですが、現在のサービスの状況を確認しないで各メソッドを実行すると例外がスローされる ことがあります。例えば、現在実行中のサービスに対してStart()メソッドを実行すると例外がスロー されます。実際は、こういうときは何もしないようにしたいので、現在のサービスの状態を確認して メソッドが実行されるような実装をしてみます。
'サービスステータスの変更 Public Function ChangeStatus(ByVal action As ServiceAction) As Boolean '(A) Dim ret As Boolean = False Try Select Case Status() '(B) Case ServiceControllerStatus.Paused '一時停止 If action.Equals(ServiceAction.Svc_Continue) Then '(C) Me.Continue() WaitForStatus(ServiceControllerStatus.Running) ret = True ElseIf action.Equals(ServiceAction.Svc_Stop) Then Me.Stop() WaitForStatus(ServiceControllerStatus.Stopped) ret = True End If Case ServiceControllerStatus.Running '実行中 If action.Equals(ServiceAction.Svc_Pause) Then If CanPauseAndContinue() Then Me.Pause() WaitForStatus(ServiceControllerStatus.Paused) ret = True End If ElseIf action.Equals(ServiceAction.Svc_Stop) Then If CanStop() Then Me.Stop() WaitForStatus(ServiceControllerStatus.Stopped) ret = True End If End If Case ServiceControllerStatus.Stopped '停止中 If action.Equals(ServiceAction.Svc_Start) Then Me.Start() WaitForStatus(ServiceControllerStatus.Running) ret = True End If Case ServiceControllerStatus.None '例外用 '(D) ret = False Case Else ret = False End Select Catch ex As Exception ret = False End Try Return ret End Function |
(A)の宣言部で引数は、先に定義した変更しようとする状態を示す ServiceAction列挙定数を指定します。
戻値は、存在しないサービスのために現在の状態を取得できなかったりしたときは Falseを、メソッドが成功した時はTrueを返します。(現在の状態が実行中で、引数に Svc_Start を指定した場合など、変更しようとする状態と現在の状態が同じ場合はFalseを返します)
(B)のStatus() は派生クラスServiceControllerExでシャドウしたプロパティです。これで現在の サービスの状態を取得して、Select Caseステートメントで場合分けをしています。
例えば、現在の状態が停止中の時、上記コードの(C)のCaseが実行されます。 引数actionは変更しようとするサービス状態を指定しますが、EqualsメソッドでSvc_Continueと 同じかどうかを確認してそうであればMe.Continue()を実行してそれ以外は何もしません。
VBAの癖で If action = Svc_Continue Then としたくなるのですが、 Equalメソッドがあるということは VB2005以降はそれを使いなさいということなんでしょうか?
次のWaitForStatusメソッドは目的の状態になるまで(サービス状態の変更が完了するまで) 時間差があるので待機するメソッドです。より細かく制御するにはタイムアウトまでの時間を設定できる オーバーロード版のWaitForStatusを使いますができるだけ単純にするために、ここでは無期限に 待機するWaitForStatusメソッドを使用しました。
以下、現在の状態が実行中、停止中の場合、それぞれactionが Svc_Pause、Svc_StopとSvc_Runningに対して上記と同様の処理を行います。(D)でStatus()プロパティで 正しく現在の状態が取得できない時、ServiceControllerStatus.Noneを返しますがこの時はメソッドの 戻値をFalseに設定します。
以上でサービスの状態の取得/変更の実装はできました。基本的に基底クラスのServiceControllerの プロパティ/メソッドをラップした程度なのでそんなに難しい点はないと思います。
次回はスタートアップの種類の取得/変更の実装をします。 オブジェクトブラウザでServiceControllerの プロパティ/メソッドを見てもそれらしいのがないので自力で実装しないといけないので大変そうに 思われますが、ある方法でできるだけ楽をしようと思います。