| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499 |
- {******************************************************************************}
- { CnPack For Delphi/C++Builder }
- { 中国人自己的开放源码第三方开发包 }
- { (C)Copyright 2001-2007 CnPack 开发组 }
- { ------------------------------------ }
- { }
- { 本开发包是开源的自由软件,您可以遵照 CnPack 的发布协议来修 }
- { 改和重新发布这一程序。 }
- { }
- { 发布这一开发包的目的是希望它有用,但没有任何担保。甚至没有 }
- { 适合特定目的而隐含的担保。更详细的情况请参阅 CnPack 发布协议。 }
- { }
- { 您应该已经和开发包一起收到一份 CnPack 发布协议的副本。如果 }
- { 还没有,可访问我们的网站: }
- { }
- { 网站地址:http://www.cnpack.org }
- { 电子邮件:master@cnpack.org }
- { }
- {******************************************************************************}
- unit CnPing;
- {* |<PRE>
- ================================================================================
- * 软件名称:网络通讯组件包
- * 单元名称:Ping 通讯单元
- * 单元作者:胡昌洪Sesame (sesamehch@163.com)
- * 备 注:定义了 TCnPing
- * 开发平台:PWin2000Pro + Delphi 5.01
- * 兼容测试:PWin9X/2000/XP + Delphi 5/6/7 + C++Builder 5/6
- * 本 地 化:该单元中的字符串均符合本地化处理方式
- * 单元标识:$Id$
- * 修改记录:2008.04.04 V1.0
- * 创建单元
- ================================================================================
- |</PRE>}
- interface
- {$I CnPack.inc}
- uses
- Windows, SysUtils, Classes, Controls, Winsock, StdCtrls, //Sockets,
- CnClasses, CnConsts, CnNetConsts;
- type
- PCnIPOptionInformation = ^TCnIPOptionInformation;
- TCnIPOptionInformation = packed record
- TTL: Byte; // Time To Live (used for traceroute)
- TOS: Byte; // Type Of Service (usually 0)
- Flags: Byte; // IP header flags (usually 0)
- OptionsSize: Byte; // Size of options data (usually 0, max 40)
- OptionsData: PAnsiChar; // Options data buffer
- end;
- PCnIcmpEchoReply = ^TCnIcmpEchoReply;
- TCnIcmpEchoReply = packed record
- Address: DWORD; // replying address
- Status: DWORD; // IP status value (see below)
- RTT: DWORD; // Round Trip Time in milliseconds
- DataSize: Word; // reply data size
- Reserved: Word;
- Data: Pointer; // pointer to reply data buffer
- Options: TCnIPOptionInformation; // reply options
- end;
- TIpInfo = record
- Address: Int64;
- IP: string;
- Host: string;
- end;
- TOnReceive = procedure(Sender: TComponent; IPAddr, HostName: string;
- TTL, TOS: Byte) of object;
- TOnError = procedure(Sender: TComponent; IPAddr, HostName: string;
- TTL, TOS: Byte; ErrorMsg: string) of object;
- //==============================================================================
- // Ping 通讯类
- //==============================================================================
- { TCnPing }
- TCnPing = class(TCnComponent)
- {* 通过调用ICMP.DLL库中的函数来实现Ping功能。}
- private
- hICMP: THANDLE;
- FRemoteHost: string;
- FRemoteIP: string;
- FIPAddress: Int64;
- FTTL: Byte;
- FTimeOut: DWord;
- FPingCount: Integer;
- FDelay: Integer;
- FOnError: TOnError;
- FOnReceived: TOnReceive;
- FDataString: string;
- FWSAData: TWSAData;
- FIP: TIpInfo;
- procedure SetPingCount(const Value: Integer);
- procedure SetRemoteHost(const Value: string);
- procedure SetTimeOut(const Value: DWord);
- procedure SetTTL(const Value: Byte);
- procedure SetDataString(const Value: string);
- procedure SetRemoteIP(const Value: string);
- function PingIP_Host(const aIP: TIpInfo; const Data; Count: Cardinal;
- var aReply: string): Integer;
- {* 以设定的数据Data(无类型缓冲区)Ping一次并返回结果。Count表示数据长度 }
- function GetReplyString(aResult: Integer; aIP: TIpInfo;
- pIPE: PCnIcmpEchoReply): string;
- {* 返回结果字符串。}
- function GetDataString: string;
- function GetIPByName(const aName: string; var aIP: string): Boolean;
- {* 通过机器名称获取IP地址}
- function SetIP(aIPAddr, aHost: string; var aIP: TIpInfo): Boolean;
- {* 通过机器名称或IP地址填充完整IP信息}
- protected
- procedure GetComponentInfo(var AName, Author, Email, Comment: string);
- override;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- function Ping(var aReply: string): Boolean;
- {* 进行循环Ping,循环次数在PingCount属性中指定。}
- function PingOnce(var aReply: string): Boolean; overload;
- {* 以设定的数据Ping一次并返回结果。}
- function PingOnce(const aIP: string; var aReply: string): Boolean; overload;
- {* 向指定IP进行一次Ping并返回结果。}
- function PingFromBuffer(var Buffer; Count: Longint; var aReply: string):
- Boolean;
- {* 以参数Buffer的数据Ping一次并读取返回结果。}
- published
- property RemoteIP: string read FRemoteIP write SetRemoteIP;
- {* 要Ping的目标主机地址,只支持ip}
- property RemoteHost: string read FRemoteHost write SetRemoteHost;
- {* 要ping的目标主机名,有主机名存在时会覆盖 RemoteIP 的内容}
- property PingCount: Integer read FPingCount write SetPingCount default 4;
- {* 调用Ping方法时进行多少次数据发送,默认是4次。}
- property Delay: Integer read FDelay write FDelay default 0;
- {* 相邻两次 Ping 间的时间间隔,单位毫秒,默认 0 也就是不延时}
- property TTL: Byte read FTTL write SetTTL;
- {* 设置的TTL值,Time to Live}
- property TimeOut: DWord read FTimeOut write SetTimeOut;
- {* 设置的超时值}
- property DataString: string read GetDataString write SetDataString;
- {* 欲发送的数据,以字符串形式表示,默认为"CnPack Ping"。}
- property OnReceived: TOnReceive read FOnReceived write FOnReceived;
- {* Ping一次成功时返回数据所触发的事件}
- property OnError: TOnError read FOnError write FOnError;
- {* Ping出错时返回的内容和信息。包括目的未知、不可达、超时等。}
- end;
- implementation
- {$R-}
- const
- SCnPingData = 'CnPack Ping.';
- ICMPDLL = 'icmp.dll';
- type
- //==============================================================================
- // 辅助过程 从icmp.dll导入的函数
- //==============================================================================
- TIcmpCreateFile = function (): THandle; stdcall;
- TIcmpCloseHandle = function (IcmpHandle: THandle): Boolean; stdcall;
- TIcmpSendEcho = function (IcmpHandle: THandle;
- DestAddress: DWORD;
- RequestData: Pointer;
- RequestSize: Word;
- RequestOptions: PCnIPOptionInformation;
- ReplyBuffer: Pointer;
- ReplySize: DWord;
- TimeOut: DWord): DWord; stdcall;
- var
- IcmpCreateFile: TIcmpCreateFile = nil;
- IcmpCloseHandle: TIcmpCloseHandle = nil;
- IcmpSendEcho: TIcmpSendEcho = nil;
- IcmpDllHandle: THandle = 0;
- procedure InitIcmpFunctions;
- begin
- IcmpDllHandle := LoadLibrary(ICMPDLL);
- if IcmpDllHandle <> 0 then
- begin
- @IcmpCreateFile := GetProcAddress(IcmpDllHandle, 'IcmpCreateFile');
- @IcmpCloseHandle := GetProcAddress(IcmpDllHandle, 'IcmpCloseHandle');
- @IcmpSendEcho := GetProcAddress(IcmpDllHandle, 'IcmpSendEcho');
- end;
- end;
- procedure FreeIcmpFunctions;
- begin
- if IcmpDllHandle <> 0 then
- FreeLibrary(IcmpDllHandle);
- end;
- //==============================================================================
- // Ping 通讯类
- //==============================================================================
- { TCnPing }
- constructor TCnPing.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- FRemoteIP := '127.0.0.1';
- FTTL := 64;
- FPingCount := 4;
- FDelay := 0;
- FTimeOut := 10;
- FDataString := SCnPingData;
- hICMP := IcmpCreateFile(); // 取得DLL句柄
- if hICMP = INVALID_HANDLE_VALUE then
- raise Exception.Create(SICMPRunError);
- end;
- destructor TCnPing.Destroy;
- begin
- if hICMP <> INVALID_HANDLE_VALUE then
- IcmpCloseHandle(hICMP);
- inherited Destroy;
- end;
- procedure TCnPing.GetComponentInfo(var AName, Author, Email,
- Comment: string);
- begin
- AName := SCnPingName;
- Author := SCnPack_Sesame;
- Email := SCnPack_SesameEmail;
- Comment := SCnPingComment;
- end;
- procedure TCnPing.SetPingCount(const Value: Integer);
- begin
- if Value > 0 then
- FPingCount := Value;
- end;
- procedure TCnPing.SetRemoteIP(const Value: string);
- begin
- if FRemoteIP <> Value then
- begin
- FRemoteIP := Value;
- if SetIP(FRemoteIP, '', FIP) then
- begin
- FRemoteHost := FIP.Host;
- FIPAddress := FIP.Address;
- end;
- end;
- end;
- procedure TCnPing.SetRemoteHost(const Value: string);
- begin
- if FRemoteHost <> Value then
- begin
- // RemoteHost 更改的话,RemoteIP 自动清空
- FRemoteHost := Value;
- if SetIP('', FRemoteHost, FIP) then
- begin
- FRemoteIP := FIP.IP;
- FIPAddress := FIP.Address;
- end;
- end;
- end;
- procedure TCnPing.SetTimeOut(const Value: DWord);
- begin
- FTimeOut := Value;
- end;
- procedure TCnPing.SetTTL(const Value: Byte);
- begin
- FTTL := Value;
- end;
- procedure TCnPing.SetDataString(const Value: string);
- begin
- FDataString := Value;
- end;
- function TCnPing.GetDataString: string;
- begin
- if FDataString = '' then
- FDataString := SCnPingData;
- Result := FDataString;
- end;
- function TCnPing.Ping(var aReply: string): Boolean;
- var
- iCount, iResult: Integer;
- sReply: string;
- begin
- aReply := '';
- iResult := 0;
- try
- SetIP(RemoteIP, RemoteHost, FIP);
- for iCount := 1 to PingCount do
- begin
- iResult := PingIP_Host(FIP, Pointer(FDataString)^, Length(DataString) * SizeOf(Char),
- sReply);
- aReply := aReply + #13#10 + sReply;
- if iResult < 0 then
- Break;
- if FDelay > 0 then
- Sleep(FDelay);
- end;
- finally
- Result := iResult >= 0;
- end;
- end;
- function TCnPing.PingOnce(var aReply: string): Boolean;
- begin
- SetIP(RemoteIP, RemoteHost, FIP);
- Result := PingIP_Host(FIP, pointer(FDataString)^, Length(DataString),
- aReply) >= 0;
- end;
- function TCnPing.PingOnce(const aIP: string; var aReply: string): Boolean;
- begin
- SetIP(aIP, aIP, FIP);
- Result := PingIP_Host(FIP, pointer(FDataString)^, Length(DataString),
- aReply) >= 0;
- end;
- function TCnPing.PingFromBuffer(var Buffer; Count: Integer;
- var aReply: string): Boolean;
- begin
- SetIP(RemoteIP, RemoteHost, FIP);
- Result := PingIP_Host(FIP, Buffer, Count, aReply) >= 0;
- end;
- function TCnPing.PingIP_Host(const aIP: TIpInfo; const Data;
- Count: Cardinal; var aReply: string): Integer;
- var
- IPOpt: TCnIPOptionInformation; // 发送数据结构
- pReqData, pRevData: PAnsiChar;
- pCIER: PCnIcmpEchoReply;
- begin
- Result := -100;
- pReqData := nil;
-
- if Count <= 0 then
- begin
- aReply := GetReplyString(Result, aIP, nil);
- Exit;
- end;
- if aIP.Address = INADDR_NONE then
- begin
- Result := -1;
- aReply := GetReplyString(Result, aIP, nil);
- Exit;
- end;
- GetMem(pCIER, SizeOf(TCnICMPEchoReply) + Count);
- GetMem(pRevData, Count);
- try
- FillChar(pCIER^, SizeOf(TCnICMPEchoReply) + Count, 0); // 初始化接收数据结构
- pCIER^.Data := pRevData;
- GetMem(pReqData, Count);
- Move(Data, pReqData^, Count); // 准备发送的数据
- FillChar(IPOpt, Sizeof(IPOpt), 0); // 初始化发送数据结构
- IPOpt.TTL := FTTL;
- try //Ping开始
- if WSAStartup(MAKEWORD(2, 0), FWSAData) <> 0 then
- raise Exception.Create(SInitFailed);
- if IcmpSendEcho(hICMP, //dll handle
- aIP.Address, //target
- pReqData, //data
- Count, //data length
- @IPOpt, //addree of ping option
- pCIER,
- SizeOf(TCnICMPEchoReply) + Count, //pack size
- FTimeOut //timeout value
- ) <> 0 then
- begin
- Result := 0; // Ping正常返回
- if Assigned(FOnReceived) then
- FOnReceived(Self, aIP.IP, aIP.Host, IPOpt.TTL, IPOpt.TOS);
- end
- else
- begin
- Result := -2; // 没有响应
- if Assigned(FOnError) then
- FOnError(Self, aIP.IP, aIP.Host, IPOpt.TTL, IPOpt.TOS, SNoResponse);
- end;
- except
- on E: Exception do
- begin
- Result := -3; // 发生错误
- if Assigned(FOnError) then
- FOnError(Self, aIP.IP, aIP.Host, IPOpt.TTL, IPOpt.TOS, E.Message);
- end;
- end;
- finally
- WSACleanUP;
- aReply := GetReplyString(Result, aIP, pCIER);
- if pRevData <> nil then
- begin
- FreeMem(pRevData); // 释放内存
- pCIER.Data := nil;
- end;
- if pReqData <> nil then
- FreeMem(pReqData); //释放内存
- FreeMem(pCIER); //释放内存
- end;
- end;
- function TCnPing.GetReplyString(aResult: Integer; aIP: TIpInfo;
- pIPE: PCnIcmpEchoReply): string;
- var
- sHost: string;
- begin
- Result := SInvalidAddr;
- case aResult of
- -100: Result := SICMPRunError;
- -1: Result := SInvalidAddr;
- -2: Result := Format(SNoResponse, [RemoteHost]);
- else
- if pIPE <> nil then
- begin
- sHost := aIP.IP;
- if aIP.Host <> '' then
- sHost := aIP.Host + ': ' + sHost;
- Result := (Format(SPingResultString, [sHost, pIPE^.DataSize, pIPE^.RTT,
- pIPE^.Options.TTL]));
- end;
- end;
- end;
- function TCnPing.GetIPByName(const aName: string;
- var aIP: string): Boolean;
- var
- pHost: PHostEnt;
- FWSAData: TWSAData;
- sName: array[0..255] of AnsiChar;
- begin
- Result := False;
- StrPCopy(sName, {$IFDEF UNICODE}AnsiString{$ENDIF}(aName));
- aIP := '';
- if aName = '' then
- Exit;
- WSAStartup($101, FWSAData);
- try
- pHost := GetHostByName(@sName);
- Result := pHost <> nil;
- if Result then
- aIP := {$IFDEF UNICODE}String{$ENDIF}(inet_ntoa(PInAddr(pHost^.h_addr_list^)^));
- finally
- WSACleanup;
- end;
- end;
- function TCnPing.SetIP(aIPAddr, aHost: string; var aIP: TIpInfo): Boolean;
- var
- pIPAddr: PAnsiChar;
- begin
- Result := False;
- aIP.Address := INADDR_NONE;
- aIP.IP := aIPAddr;
- aIP.Host := aHost;
- if aIP.IP = '' then
- begin
- if (aIP.Host = '') or (not GetIPByName(aIP.Host, aIP.IP)) then
- Exit;
- end;
- GetMem(pIPAddr, Length(aIP.IP) + 1);
- try
- ZeroMemory(pIPAddr, Length(aIP.IP) + 1);
- StrPCopy(pIPAddr, {$IFDEF UNICODE}AnsiString{$ENDIF}(aIP.IP));
- aIP.Address := inet_addr(PAnsiChar(pIPAddr)); // IP转换成无点整型
- finally
- FreeMem(pIPAddr); // 释放申请的动态内存
- end;
- Result := aIP.Address <> INADDR_NONE;
- end;
- initialization
- InitIcmpFunctions;
- finalization
- FreeIcmpFunctions;
- end.
|