Am arătat în articolul precedent cum se poate utiliza WMI pentru testarea disponibilității unui device dintr-o rețea. În articolul curent prezint codul sursă a unei aplicații ce permite lansarea de comenzi ping către diferite IP-uri prin multi thread-uri.
program project1;
{$mode objfpc}{$H+}
uses Classes, SysUtils, CustApp, Windows, ActiveX, ComObj, GetOpts;
Type TPingThread = Class(TThread) Computer: String; Procedure Execute; Override; End;
Const Buffer: Integer= 32; Timeout: Integer= 100; Retries: Integer= 4; MaxThreads: Integer= 30;
Var c: Char; OptionIndex: Longint; TheOpts: Array[1..5] Of TOption; i, ActiveThreads, IpOffline, IpOnLine: Integer; IpList: TStringList; t: TPingThread; Start: Variant;
Function WmiPing(Const Address: String; Const BufferSize, Timeout: Word): Integer; Const WmiUser = ''; WmiPassword = ''; WmiComputer = 'localhost'; WmiFlagForwardOnly = $00000020; Var WmiLocator : OLEVariant; WmiService : OLEVariant; WmiObjectSet: OLEVariant; WmiObject : Variant; oEnum : ActiveX.IEnumvariant; WmiQuery : String[250]; begin Result:= -1; CoInitialize(Nil); Try WmiLocator := ComObj.CreateOleObject('WbemScripting.SWbemLocator'); WmiService := WmiLocator.ConnectServer(WmiComputer, 'root\CIMV2', WmiUser, WmiPassword); WmiQuery := Format('Select * From Win32_PingStatus '+ 'Where Address=%s And BufferSize=%d And TimeOut=%d', [QuotedStr(Address), BufferSize, Timeout]); WmiObjectSet:= WmiService.ExecQuery(WmiQuery, 'WQL', WmiFlagForwardOnly); oEnum := IUnknown(WmiObjectSet._NewEnum) As IEnumVariant; While oEnum.Next(1, WmiObject, Nil) = 0 Do Begin Result:= LongInt(WmiObject.Properties_.Item('StatusCode').Value); WmiObject:= Unassigned; End; Finally CoUninitialize; End; end;
Procedure TPingThread.Execute; Var i, k: Integer; begin k:= 0; For i:= 1 To Retries Do If WmiPing(Computer, Buffer, Timeout)= 0 Then Inc(k);
If k= 0 Then Inc(IpOffLine) Else Inc(IpOnLine);
Writeln(Computer, ';', k, ';', Retries); Dec(ActiveThreads); End;
begin Start:= GetTickCount; IpList:= TStringList.Create;
With TheOpts[1] Do Begin Name:= 'ipfile'; Has_Arg:= 1; Flag:= Nil; Value:= #0; End; With TheOpts[2] Do Begin Name:= 'buffer'; Has_Arg:= 1; Flag:= Nil; Value:= #0; End; With TheOpts[3] Do Begin Name:= 'timeout'; Has_Arg:= 1; Flag:= Nil; Value:= #0; End; With TheOpts[4] Do Begin Name:= 'retries'; Has_Arg:= 1; Flag:= Nil; Value:= #0; End; With TheOpts[5] Do Begin Name:= 'maxthreads'; Has_Arg:= 1; Flag:= Nil; Value:= #0; End;
c:= #0; Repeat c:= GetLongopts(':i:b:t:r:m:', @TheOpts[1], OptionIndex); Case c Of 'i': If FileExists(OptArg) Then Begin IpList.LoadFromFile(OptArg); If IpList.Count= 0 Then Begin Writeln('Empty IP File: ', OptArg); Exit; End; End Else Begin Writeln('IP File not found: ', OptArg); Exit; End;
'b': Begin Buffer:= StrToInt(OptArg); If (Buffer< 0) Or (Buffer> 65500) Then Begin Writeln('Ping Buffer ', OptArg, ' out of limit 0..65500'); Exit; End; End; 't': Begin Timeout:= StrToInt(OptArg); If (Timeout< 10) Or (Timeout> 10000) Then Begin Writeln('Ping Timeout ', OptArg, ' out of limit 10..10000'); Exit; End; End; 'r': Begin Retries:= StrToInt(OptArg); If (Retries< 1) Or (Retries> 10) Then Begin Writeln('Ping Retries ', OptArg, ' out of limit 1..10'); Exit; End; End; 'm': Begin MaxThreads:= StrToInt(OptArg); If (MaxThreads< 1) Or (MaxThreads> 500) Then Begin Writeln('Max Threads ', OptArg, ' out of limit 1..500'); Exit; End; End; End; { case } Until c= EndOfOptions;
ActiveThreads:= 0; IpOffline:= 0; IpOnLine:= 0; i:= 0; Writeln('Computer;Success;Total'); While i< IpList.Count Do Begin If ActiveThreads< MaxThreads Then Begin t:= TPingThread.Create(True); Inc(ActiveThreads); Inc(i); t.Computer:= IpList[i- 1]; t.FreeOnTerminate:= True; t.Start; End; End;
While ActiveThreads> 0 Do ;
WriteLn; WriteLn('Ip Count= ', IpList.Count); WriteLn('Ip Online= ', IpOnLine); WriteLn('Ip Offline= ', IpOffline); WriteLn('Buffer= ', Buffer); WriteLn('Timeout= ', Timeout, ' ms'); WriteLn('Retries= ', Retries); WriteLn('Max Threads= ', MaxThreads); WriteLn('Elapsed Time= ', (GetTickCount- Start)/ 1000, 's');
IpList.Free; end.
Pentru a-l putea folosi descărcați ultima versiune de Lazarus IDE. Eu am compilat programul cu v1.2.0 pe 64 biți. După compilare, din linia de comandă se utilizează astfel:
Pinguin64.exe -i "IP LIST.txt" -b 32 -t 100 -r 4 -m 500 > "IP RESULT.TXT"
unde “IP LIST.txt” este un fișierul care conține lista de IP-uri țintă, iar “IP RESULT.TXT” este fișierul în care vor fi vărsate rezultatele. Parametrii -b 32 arată faptul că se trimit 32 biți prin ping, -t 100 reprezintă timeout-ul ping-ului în milisecunde, -r 4 reprezintă numărul de încercări succesive efectuate către fiecare IP în parte, iar -m 500 numărul de thread-uri utilizate. Atenție: un număr prea mare de thread-uri se poate să nu poată fi încărcat în memoria computerului dvs!
sdfsdfsd