・前準備
今回もExcel 2000 - Windows XPという環境で行います。何はともあれ、まずは WMI スクリプトライブラリを使えるように参照設定しないといけません。 新しいBookを作成してAlt + F11でVBエディタを起動します。起動したら、ツール(T)→参照設定(R)で 参照設定のダイアログを表示させます。
コンポーネントの中で Microsoft WMI Scripting V1.x Library を見つけてチェックをしてOKをクリックしてください。それではオブジェクトブラウザで 確認しながら実装します。
プログラムの流れとしては
1.WMI名前空間(root\cimv2)への接続
2.その名前空間内のWin32_ServiceクラスのインスタンスのCollectionを取得。
3.目的のインスタンス(サービス)への参照を取得。
4.サービスの状態、スタートアップの種類をあらわすState、StartModeの各プロパティを実行。
という流れになります。
・サービスの状態/スタートアップの種類の取得
今回もSQL Server 2005 Expressのサービス情報を取得することにします。 まず、前頁の CIM Studioを使ってSQL Server 2005 Expressの インスタンスを表示して、各プロパティを確認しておきましょう。 目的のインスタンスにたどり着くまでの過程をプログラミングしていくので 雰囲気をつかんでおいてください。
今回はサービスの状態の取得もスタートアップの種類の取得もほぼ同様のコードなので 引数で取得するプロパティの種類を選択するようにしました。 標準モジュールを追加してそこに以下を記述してください。
'サービスの状態/スタートアップの種類の取得 '引数 argsName : サービス名 ' argsPropType : 取得するプロパティ名(StateまたはStartMode) '戻値 第二引数 - Stateの場合 ' Running , Stopped, Paused, None(エラー時) ' StartModeの場合 ' Auto , Manual, Disabled, None(エラー時) Public Function GetServiceValue(argsName As String, _ argsPropType As String) As String Dim voLocator As WbemScripting.SWbemLocator Dim voServices As WbemScripting.SWbemServices '名前空間上のオペレーション Dim voInstances As WbemScripting.SWbemObjectSet 'サービスインスタンス群 Dim voInstance As WbemScripting.SWbemObject 'サービスインスタンス On Error Resume Next GetServiceValue = "None" 'SWbemLocatorオブジェクト作成 Set voLocator = CreateObject("WbemScripting.SWbemLocator") '(A) If Err.Number <> 0 Then Exit Function 'root\cimv2名前空間に接続 Set voServices = voLocator.ConnectServer(strServer:=".", _ strNamespace:="root\cimv2") '(B) If Err.Number <> 0 Then Exit Function 'Win32_Serviceクラスのインスタンス群の取得 Set voInstances = voServices.InstancesOf("Win32_Service") '(C) '各インスタンスから対象のサービスを取得 For Each voInstance In voInstances If Err.Number <> 0 Then Exit Function '対象のサービスのプロパティの値を取得 If voInstance.Properties_("Name") = argsName Then '(D) GetServiceValue = voInstance.Properties_(argsPropType): Exit For End If Next voInstance Set voLocator = Nothing End Function |
(A)で名前空間への接続のためのSWbemLocatorを作成し、(B)でroot\cimv2名前空間に
接続します。第一引数の"."はローカルコンピュータを示します。
root\cimv2名前空間内のWin32_Serviceクラスのインスタンス群を(C)で取得します。
For EachステートメントでNameプロパティにより目的のインスタンスを判断しStateまたはStartMode
プロパティの値を取得します。CIM StudioでNameプロパティを見ると鍵マークがついているのは
このプロパティの値がキーという意味で(D)でインスタンスを特定できます。
Win32_Serviceのインスタンス数はそれほど多くないのでFor Eachで総当りしても それほど時間はかかりませんが、EventLogあたりになるとそうとうたいへんです。そこで目的の インスタンス名(サービス名)が分かっているのでしたら(C)の部分を以下のように変更して クエリを発行したほうがいいかもしれません。
Dim vsSQL As String vsSQL = "SELECT * FROM Win32_Service WHERE Name = '" & argsName & "'" Set voInstances = voServices.ExecQuery(vsSQL) |
あるいは、せっかくCIM Studioを使っているのでしたら目的のインスタンスの プロパティの下のほうにある __PATH で表示されている文字列を参考にして直接目的の インスタンスを取得する方法がベストかと思います。
'サービスの状態/スタートアップの種類の取得 '引数 argsName : サービス名 ' argsPropType : 取得するプロパティ名(StateまたはStartMode) '戻値 第二引数 - Stateの場合 ' Running , Stopped, Paused, None(エラー時) ' StartModeの場合 ' Auto , Manual, Disabled, None(エラー時) Public Function GetServiceValue(argsName As String, _ argsPropType As String) As String Dim voLocator As WbemScripting.SWbemLocator Dim voServices As WbemScripting.SWbemServices '名前空間上のオペレーション 'Dim voInstances As WbemScripting.SWbemObjectSet 'サービスインスタンス群 Dim voInstance As WbemScripting.SWbemObject 'サービスインスタンス Dim vsPath As String On Error Resume Next GetServiceValue = "None" 'SWbemLocatorオブジェクト作成 Set voLocator = CreateObject("WbemScripting.SWbemLocator") If Err.Number <> 0 Then Exit Function 'root\cimv2名前空間に接続 Set voServices = voLocator.ConnectServer(strServer:=".", _ strNamespace:="root\cimv2") If Err.Number <> 0 Then Exit Function '目的のインスタンスへのPath vsPath = "\\" & Environ("COMPUTERNAME") _ & "\root\cimv2:Win32_Service.Name='" & argsName & "'" |
VBエディタからCtrl + Gでイミディエイトウィンドウから実行してみます。
・サービスの状態の変更
次にサービスの状態とスタートアップの種類の変更はWMIではどうやるかということですが、 これもCIM Studioが助けてくれます。まず、 ここの手順でSQL Server 2005 Expressの インスタンスを右ペインに表示させます。その上部にMethodsタブがあるのでそれをクリックすると, インスタンスが実行できるメソッド名を確認できます。それをみるとPauseService, ResumeService, StartService, StopServiceがサービスの状態を変更するメソッドだとわかると思います。
前項で分かるように WMI スクリプトライブラリではサービスのインスタンスへの参照はSWbemObject に代入されます。今回はたまたまサービスを扱ってますが、SWbemObjectはさまざまなクラスの インスタンスへの参照が格納されます。これらのクラスの違う固有のメソッドを実行するために SWbemObjectにはExecMethodがあるのでこれを使います。
サービスの状態を変更してから要求した状態になるまでのタイムラグのために以前にも 使ったAPIのSleep関数を使うので宣言部に以下を記述してください。
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) |
以下が実装です。
'サービス状態変更 '引数 argsName : サービス名 ' argsState : 変更する状態(Run, Continue, Pause, Stop)のいずれか '戻値 成功 True, 失敗 False Public Function ChangeServiceStatus(argsName As String, argsState) As Boolean Dim voLocator As WbemScripting.SWbemLocator Dim voServices As WbemScripting.SWbemServices Dim voInstance As WbemScripting.SWbemObject Dim vsPath As String Dim voRet As WbemScripting.SWbemObject 'リターンオブジェクト On Error Resume Next 'SWbemLocatorオブジェクト作成 Set voLocator = CreateObject("WbemScripting.SWbemLocator") If Err.Number <> 0 Then Exit Function 'root\cimv2名前空間に接続 Set voServices = voLocator.ConnectServer(strServer:=".", _ strNamespace:="root\cimv2") If Err.Number <> 0 Then Exit Function vsPath = "\\" & Environ("COMPUTERNAME") _ & "\root\cimv2:Win32_Service.Name='" & argsName & "'" Set voInstance = voServices.Get(vsPath) If Err.Number <> 0 Then Exit Function 'サービスの状態によって分岐 Select Case voInstance.Properties_("State") Case "Stopped" '現在停止中 If argsState = "Run" Then '実行要求 Set voRet = voInstance.ExecMethod_("StartService") '(A) If voRet.Properties_("ReturnValue").Value <> 0 Then Exit Function Do Until GetServiceValue(argsName, "State") = "Running" Sleep voInstance.Properties_("WaitHint") Loop ChangeServiceStatus = True End If Case "Paused" '現在一時停止 If argsState = "Continue" Then '再開要求 Set voRet = voInstance.ExecMethod_("ResumeService") If voRet.Properties_("ReturnValue").Value <> 0 Then Exit Function Do Until GetServiceValue(argsName, "State") = "Running" Sleep voInstance.Properties_("WaitHint") Loop ChangeServiceStatus = True ElseIf argsState = "Stop" Then '停止要求 Set voRet = voInstance.ExecMethod_("StopService") If voRet.Properties_("ReturnValue").Value <> 0 Then Exit Function Do Until GetServiceValue(argsName, "State") = "Stopped" Sleep voInstance.Properties_("WaitHint") Loop ChangeServiceStatus = True End If Case "Running" '現在実行中 If argsState = "Pause" Then '一時停止要求 Set voRet = voInstance.ExecMethod_("PauseService") If voRet.Properties_("ReturnValue").Value <> 0 Then Exit Function Do Until GetServiceValue(argsName, "State") = "Paused" Sleep voInstance.Properties_("WaitHint") Loop ChangeServiceStatus = True ElseIf argsState = "Stop" Then '停止要求 Set voRet = voInstance.ExecMethod_("StopService") If voRet.Properties_("ReturnValue").Value <> 0 Then Exit Function Do Until GetServiceValue(argsName, "State") = "Stopped" Sleep voInstance.Properties_("WaitHint") Loop ChangeServiceStatus = True End If Case Else End Select End Function |
注目点は(A)のExcecMethodです。CIM Studioで確認したWin32_Service固有の メソッドを実行しています。戻ってきたオブジェクト(voRet)にはReturnValueプロパティがあり、 ExecMethodの実行結果の如何を判断できます。
・スタートアップの種類の変更
最後のスタートアップの種類の変更ですが、これもCIM Studioで目的の サービスインスタンスのメソッドタブを見てみると、「ChangeStartMode」があります。 これを使うことになりますが、サービスの状態の変更のときと違い、メソッドが1つしかないので どの種類に変更するのかは引数で指定しなければならないのだろうという想像はつきます。 では、メソッド名だけを指定するExcecMethodでどうやって引数を設定するのかというと、 第二引数に引数の値を保持したSWbemObjectを指定するようになっています。
どんな引数を指定しなければいけないということは再びCIM Studioで確認できます。 同様にCIM Studioのメソッドタブで、ChangeStartModeを右クリックすると 「Edit Method Parameters」という項目があるので選択します。すると、 StartModeという引数名とデフォルトの値があるので確認しておきます。では実装します。
'スタートアップの種類の変更 '引数 argsName : サービス名 ' argsStartType 変更するスタートアップの種類 ' (Automatic Manual Disabledのいずれか) '戻値 成功 True 失敗 False Public Function ChangeStartMode(argsName As String, _ argsStartType As String) As Boolean Dim voLocator As WbemScripting.SWbemLocator Dim voServices As WbemScripting.SWbemServices Dim voInstance As WbemScripting.SWbemObject 'サービスインスタンス Dim vsPath As String Dim voParams As WbemScripting.SWbemObject 'メソッドのパラメータ Dim voRet As WbemScripting.SWbemObject 'メソッド実行の戻値 On Error Resume Next Set voLocator = CreateObject("WbemScripting.SWbemLocator") If Err.Number <> 0 Then Exit Function Set voServices = voLocator.ConnectServer(strServer:=".", _ strNamespace:="root\cimv2") If Err.Number <> 0 Then Exit Function vsPath = "\\" & Environ("COMPUTERNAME") _ & "\root\cimv2:Win32_Service.Name='" & argsName & "'" Set voInstance = voServices.Get(vsPath) If Err.Number <> 0 Then Exit Function 'Win32_ServiceクラスのChangeStartModeメソッドのパラメータオブジェクト Set voParams = voInstance.Methods_("ChangeStartMode").InParameters '(A) voParams.Properties_("StartMode").Value = argsStartType 'パラメータを設定してメソッド実行 Set voRet = voInstance.ExecMethod_("ChangeStartMode", voParams) '(B) If voRet.Properties_("ReturnValue") <> 0 Then Exit Function ChangeStartMode = True End Function |
サービスインスタンスのメソッドの引数に指定する値は、(A)で示されるように、 CIM Studioで確認したメソッド名でSWbemMethodオブジェクト (voInstance.Methods_("ChangeStartMode"))のInParametersプロパティから パラメータに指定するSWbemObjectを取得して、同じくCIM Studioで確認したStartModeという 引数名で対応する値を設定します。最後に(B)でChangeStartModeを実行します。
・最後に
今回はWMIの利用例をWindowsサービスについてのみ書きましたが、 CIM Studioのクラス群をみてみればWindowsシステムの相当な情報の取得や変更ができる たいへん便利な機能だとわかると思います。個人的には、少々重いという欠点はあるものの それを差し引いてもあまりある便利さだと思います。
WMIを利用するには WMI スクリプトライブラリを利用する機会が多いとす思いますが、今のところ レファレンスが英語版しか確認してせん。ですが、この便利さを享受するために 理解されることをお勧めします。
次はいよいよ最後になりますが、VC++でのWindows サービスの制御について書きたいと思います。
参考文献
Windows Management Instrumentation(英語)
http://msdn.microsoft.com/en-us/library/aa394582(VS.85).aspx
WMI スクリプト入門 : 第 1 部
http://msdn.microsoft.com/ja-jp/library/ms974579.aspx
WMI スクリプト入門 : 第 2 部
http://msdn.microsoft.com/ja-jp/library/ms974592.aspx
WMI スクリプト入門 : 第 3 部
http://msdn.microsoft.com/ja-jp/library/ms974547.aspx
Windows Management Instrumentationの秘密
http://www.microsoft.com/japan/technet/scriptcenter/resources/wmifaq.mspx