趣味の話題や忘備録(的な)情報のページ
くろねこ自由気ままな日記
ブログランキング・にほんブログ村へ

vbsによるWMIの活用(1) ・・・ pingの実行

皆さん、こんにちは。「ソフトウェア」カテゴリ 初投稿です(祝)

f:id:kuronekofreedom:20200705163755j:plainマニアックタイム です(笑)

はじめに、くろねこ自称プログラマです。

もう長いです(汗)扱ってきた言語もいろいろやっていますが、古いものばかりですね ^^;

IT関連会社を離れ、別の業種の今は、最新のプログラミング言語事情や開発環境からはすっかり遠のいてます。
それなので、最近は簡単に「ちょろっと」使えるもので、「ちょっとしたツール的なもの」で効率化を図ってるので、ExcelVBAやバッチが中心です。
ExcelVBAはその名の通り、Visual Basicファミリの一つで、Visual Basic for Applicationsの略です。Visual Basic(以下、VB)はバージョン6.0までのものと、VB.Netで大きく分かれます。

くろねこの周辺では、前者を「VB」、後者を「VB.Net」と呼んでる人が多いです。
ちなみに、くろねこはVB側の人間で(古!)、VB2.0~VB6.0で変なもの作ってました(汗)

VBAは、Microsoft Officeファミリに実装されたVBで、基本的な文法や構造を継承し、実装されたアプリケーションのオブジェクトをそのまま扱えるようになっています。例えば、ExcelならWorkBookやWorkSheetなどのオブジェクトです。
VBAは、Excelのほかにも、Word、AccessPowerPointOutlookにも実装されているので、オブジェクトの扱い方さえ理解すれば、「大体のこと」は実現できます。
そのほかには、VBScript(VBS)というVBのサブセット版的なものが、ほぼすべてのWindowsに実装されてます。
くろねこはこのVBSを駆使していろんなことやってます!
今回はその簡単な例をご紹介しますね。

WMIを使ってみよう!

WMIって聞いたことありますか?

Windows Management Instrumentation の略で、Windowsのいろいろな情報を取得・設定するためのものです。詳しいことは「WMIとは」でグーグル先生に聞いてみてください。
今回の記事は、WMIを使って対象のPCにpingしてみよう! です。もちろんVBSで汎用的なサンプルを作る企画です。出来上がったら使ってみてください。

利用シチュエーションはこんな感じです。
PC-01,PC-02,PC-03,・・・,PC-10 の10台のうち、停止しているPCの一覧を表示する。

f:id:kuronekofreedom:20200702231624j:plain
pingコマンドの実行

仕様

プログラムの仕様を検討します。

求められる要件は次のようになります。

  1. 汎用性を考えると単機能のものを用意し、それを必要な数だけ繰り返す。というのが自然です。
  2. 上記のシチュエーションのように、PC名(マシン名)での指定が可能とする。
  3. PC以外のネットワークデバイスにもpingで確認することはよくあることなので、IPアドレスでの指定も可能とする。

それでは、上記1~3を実現するプログラムをVBSで作成しましょう。プログラム名称は「GetPCStatus.vbs」とします。
実行イメージは以下のようになります。

<span style="color: #1464b3"><b>cscript GetPCStatus.vbs PC名</b></span>

プログラムのパーツ

VBSでの引数の取得

VBSでプログラム起動時に指定された引数の取得は、WScript.Argumentsオブジェクトを使用します。
もちろん、プログラムの一番最初の部分でこのオブジェクトを使って引数を取得します。WScript.Argumentsを簡単に説明すると以下のようになります。

  • WScript.Arguments.Count : 指定された引数の数
  • WScript.Arguments.Item() : 指定された引数が格納されている配列

引数は1つのみなので、WScript.Arguments.Item(0)で参照できます。
下記のサンプルは、引数1を変数wkStrに設定するものです。

Set mvParam = WScript.Arguments
If mvParam.Count > 0 Then
  wkStr = mvParam.Item(0)
Else
  WScript.Echo "引数は指定されませんでした"
End If

WMIでping(ICMP)を実行

今回、WMIを使ってpingを実行するので、その心臓部となる部分を先に作成します。
下記のサンプルは、IPアドレスが192.168.1.1の機器にpingを実行するものです。

Set pvObjLocator = CreateObject("WbemScripting.SWbemLocator") 
Set pvObjServer = pvObjLocator.ConnectServer()
Set pvObjPing = pvObjServer.ExecQuery("Select * From Win32_PingStatus Where Address='192.168.1.1' And Timeout=500")
For Each pvPing In pvObjPing
  Script.Echo pvPing.StatusCode
Next
Set pvPing = Nothing
Set pvObjPing = Nothing
Set pvObjServer = Nothing
Set pvObjLocator = Nothing

IPアドレス部分をPC名で指定すると、このVBSを実行しているPCがDNSサーバに名前解決の問い合わせをしてくれるので、要件3は意識せずに実現できます。

プログラムの復帰値の設定

VBSで復帰値を設定するには、WScript.Quit の引数で復帰値を指定します。
具体的には以下のような感じです。(復帰値で12を設定する。)

WScript.Quit 12

サンプルプログラム(GetPCStatus.vbs)

ここまで説明した内容で、一通り動作するサンプルプログラムです。
このプログラムのままでも利用可能ですが、いろいろ改良してみてください。

Option Explicit
'***************************************************************************************************
'  GetPCStatus.vbs
'  指定したIPアドレス(マシン名)へのpingを実施し、その結果を復帰値として返却する。
'  <使用方法>
'      cscript //NOLOGO GetPCStatus.vbs IPAddr
'        IPAddr : 対象PCのIPアドレス、または、マシン名
'  <復帰値>
'           0 :   応答あり
'       32767 :  Usage表示(引数なし時)
'       65535 :  名前解決エラー
'      その他 :  応答なし(詳細はICMPの状態コードを参照)

'***************************************************************************************************
  Const mcICMPtimeout = 500
  Const mcUsage_000 = "[Command usage]"
  Const mcUsage_010 = "cscript //NOLOGO GetPCStatus.vbs { IPAddress | PCName }"
  Const mcNameError = 65535
  Const mcShowUsage = 32767
  Dim mvAns
  Dim mvParam

  mvAns = mcShowUsage
  Set mvParam = WScript.Arguments
  If mvParam.Count > 0 Then
    mvAns = IssueICMP(mvParam.Item(0),mcICMPtimeout)
  Else
    Call Echo(mcUsage_000)
    Call Echo(mcUsage_010)
  End If
  Set mvParam = Nothing

  WScript.Quit mvAns

Public Function IssueICMP (pIPAddr, pTimeout)
'***************************************************************************************************
'  IssueICMP
'  指定したIPアドレスへのpingを実施する。
'  <引数>
'    pIPAddr :  IPアドレス または マシン名
'    pTimeout :  タイムアウト時間(ms)
'  <復帰値>
'    ICMPのStatuscodeの値
'    ※ 名前解決できない場合は65535を返却
'***************************************************************************************************

  Const pcWQL = "Select * From Win32_PingStatus Where Address='%0%' And Timeout=%1%"
  Dim pvAns
  Dim pvWQL
  Dim pvAddr
  Dim pvObjLocator
  Dim pvObjServer
  Dim pvObjPing
  Dim pvPing
   
  pvAns = mcNameError
  pvWQL = pcWQL
  pvWQL = Replace(pvWQL,"%0%",pIPAddr)
  pvWQL = Replace(pvWQL,"%1%",FormatNumber(pTimeout,0,-1,0,0))

  'WMIでICMPを実行
  Set pvObjLocator = CreateObject("WbemScripting.SWbemLocator")
  Set pvObjServer = pvObjLocator.ConnectServer()
  Set pvObjPing = pvObjServer.ExecQuery(pvWQL)
  For Each pvPing In pvObjPing
    pvAns = pvPing.StatusCode
  Next
  Set pvPing = Nothing
  Set pvObjPing = Nothing
  Set pvObjServer = Nothing
  Set pvObjLocator = Nothing
  On Error GoTo 0

  '名前解決エラーの場合、pvAnsの値を補正
  On Error Resume Next
  pvAns = Int(FormatNumber(pvAns,0,-1,0,0))
  If Err.Number <> 0 Then
    pvAns = mcNameError
  End If
  On Error GoTo 0

  IssueICMP = pvAns

End Function

Public Sub Echo (pStr)
'***************************************************************************************************
'  Echo
'  指定した文字列を画面に出力する。
'  <引数>
'    pStr :  出力する文字列
'***************************************************************************************************
  WScript.Echo pStr
End Sub

実行用バッチファイル

GetPCStatus.vbsを実行するサンプルバッチファイルです。

@echo off
  cd /d %~dp0

  cscript //NOLOGO GetPCStatus.vbs %1

  IF ERRORLEVEL 32768 GOTO NAMEERR
  IF ERRORLEVEL 16384 GOTO USAGE
  IF ERRORLEVEL 1 GOTO OFFLINE

  ECHO %1 is online (return : %ERRORLEVEL%)
  GOTO ENDING

:NAMEERR
  ECHO %1 is name error (return : %ERRORLEVEL%)
  GOTO ENDING

:USAGE
  ECHO Show usage (return : %ERRORLEVEL%)
  GOTO ENDING

:OFFLINE
  ECHO %1 is offline (return : %ERRORLEVEL%)
  GOTO ENDING

:ENDING

活きてるのにpingが通らない場合

GetPCStatus.vbsの実行結果 ≠ pingコマンドの実行結果 の場合は下記を確認しましょう。

  • pingの宛先が、ping(ICMP)を受け付ける設定になっているか、確認しましょう。
  • WIndows系のPCやサーバの場合は、RPCおよびWMIサービスが実行されているか、確認しましょう。

応用例

サンプルプログラム(GetPCStatus.vbs)を応用して、ちょっとした監視ツール(所謂、死活監視)にすることも可能です。
ただし、このままですと「応答なし」の検出が高くなるので、対象機器に複数回pingを実行し、すべて失敗した場合に初めてオフラインと判断するような考慮を付け加える必要があります。
ping -t で対象にpingを投げ続けた場合、時々、エラーになることがありますよね。その瞬間だけで判断すると「活きてるのに死んでいる?」ことが起きます。

また、近いうちにVBネタをやりたいと思います。
このエントリーをはてなブックマークに追加


f:id:kuronekofreedom:20200415221044j:plain くろねこ自由気ままな日記

ブログランキング・にほんブログ村へ