マシンの死活監視に欠かせないのがPing。
くろねこも社内でアプリケーションサーバーがDBサーバーに接続しに行く前に死活監視を...と言う話を切り出してからはなかなか実現できず、問題だけが積み上がっていた。
だって、それに集中する時間が無いんだもん...
すでに、社内のマネージャからは「ライフワーク」といわれ始めた...
そんな状況を打開するために、ちょっと気合いを入れて調査してみる。
よくあるPingのサンプルプログラムでは管理者権限が必要。
管理者権限が不要といわれているプログラムをいくつか試してみたが動かず暗礁に乗り上げていたが...
本日、先日買ったノートPCで帰宅中に、最悪.net Frameworkでもいいか~と調べていると...
やっぱり、Pingクラスってあるんですね。
さらに、Pingクラスを使っている例が無いかググってみると、WMIで実行する方法がある。
WMIはWin32でも実行できるので、今回はこちらでコーディングしてみることにした。
できあがったソース。
なお、このプログラムのコンパイルにはタイプライブラリの取り込みが必要です。
Microsoft WMI Scripting V1.2 Library を取り込み、作成されたユニットをプロジェクトと同じフォルダに置くことを前提に作成してある。
※取り込み方法:メニューの「コンポーネント|コンポーネントのインポート」で、コンポーネントのインポートウィザードを表示し、次にタイプライブラリのインポートを選択し「次へ」、一覧からMicrosoft WMI Scripting V1.2 Library を選択し「次へ」、ユニットの作成を選択し「完了」ボタンをクリック。
program PingExec; {$APPTYPE CONSOLE} uses SysUtils, Variants, ComObj, ActiveX, WbemScripting_TLB in 'WbemScripting_TLB.pas'; type // 参考 // Win32_PingStatus = record // string Address; // uint32 BufferSize = 32; // boolean NoFragmentation = FALSE; // uint32 PrimaryAddressResolutionStatus; // string ProtocolAddress = ""; // string ProtocolAddressResolved = ""; // uint32 RecordRoute = 0; // boolean ReplyInconsistency; // uint32 ReplySize; // boolean ResolveAddressNames = FALSE; // uint32 ResponseTime; // uint32 ResponseTimeToLive; // string RouteRecord[]; // string RouteRecordResolved[]; // String SourceRoute = ""; // uint32 SourceRouteType = 0; // uint32 StatusCode; // uint32 Timeout = 1000; // uint32 TimeStampRecord[]; // string TimeStampRecordAddress[]; // string TimeStampRecordAddressResolved[]; // uint32 TimeStampRoute = 0; // uint32 TimeToLive = 80; // uint32 TypeofService = 0; // end; TPingResult = record BufferSize: cardinal; ProtocolAddress: string; ResponseTime: cardinal; ResponseTimeToLive: cardinal; end; function SendPing(address: string; var pr: TPingResult): integer; var Locator: ISWbemLocator; Services: ISWbemServices; SObjSet: ISWbemObjectSet; SObject: ISWbemObject; Enum: IEnumVariant; TempObj: OleVariant; TempVal: Cardinal; Query: string; begin Result := -1; if Failed(CoInitialize(nil)) then Exit; try Locator := CoSWbemLocator.Create; Services := Locator.ConnectServer('.', 'root\cimv2', '', '', '', '', 0, nil); Query := 'SELECT * FROM Win32_PingStatus WHERE address=' + QuotedStr(address); SObjSet := Services.ExecQuery(Query, 'WQL', wbemFlagReturnImmediately and wbemFlagForwardOnly , nil); TempVal := 0; Enum := (SObjSet._NewEnum) as IEnumVariant; if (Succeeded(Enum.Next(1, TempObj, TempVal)) and (TempVal > 0)) then begin try SObject := IUnknown(TempObj) as ISWBemObject; if (SObject <> nil) then begin if VarIsNull(SObject.Properties_.Item('StatusCode', 0).Get_Value) then exit; Result := (SObject.Properties_.Item('StatusCode', 0).Get_Value); if (Result = 0) then begin with SObject.Properties_ do begin pr.ProtocolAddress := VarToStr(Item('ProtocolAddress', 0).Get_Value); pr.BufferSize := StrToIntDef(VarToStr(Item('BufferSize', 0).Get_Value), 0); pr.ResponseTime := StrToIntDef(VarToStr(Item('ResponseTime', 0).Get_Value), 0); pr.ResponseTimeToLive := StrToIntDef(VarToStr(Item('ResponseTimeToLive', 0).Get_Value), 0); end; end; end; finally SObject := nil; VarClear(TempObj); end; end; finally Enum.Reset; SObjSet := nil; Services := nil; Locator := nil; end; end; (** Main **) var pr: TPingResult; begin if (SendPing(ParamStr(1), pr) = 0) then begin System.Writeln(Format('%s からの応答: バイト数 =%d 時間 =%dms TTL=%d', [ pr.ProtocolAddress, pr.BufferSize, pr.ResponseTime, pr.ResponseTimeToLive])); end else begin System.Writeln(Format('%s からの応答: 宛先ホストに到達できません。', [ParamStr(1)])); end; end.
RFC 791準拠.ちなみにパラメータはコンピュータ名でもIPアドレスでも実行可能。IPv6 and IPv4 Support in WMIということなのでIPアドレスはIPv4, IPv6 双方可能だと思われる。
■参考URI