[杂谈] 调用 Windows API Ping 指定的主机

一段简单的代码,调用 API 来实现 PING 指定的主机。

[C++ Builder 版]

#include <iphlpapi.h>
#include <icmpapi.h>
#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "ws2_32.lib")
// ---------------------------------------------------------------------------

int __fastcall Ping(const AnsiString AHost) {
	HANDLE hIcmp = IcmpCreateFile();
	int ARetVal;
	if (hIcmp != INVALID_HANDLE_VALUE) {
		try {
			char AData[32] = "ICMP Data";
			DWORD Addr = inet_addr(AHost.c_str());
			if (Addr == INADDR_NONE) {
				hostent *ent = gethostbyname(AHost.c_str());
				if (ent && (ent->h_addrtype == AF_INET)) {
					Addr=*((DWORD *)ent->h_addr_list[0]);
				}
				else {
					ARetVal = GetLastError();
					return ARetVal;
				}
			}
			DWORD AReplySize = sizeof(ICMP_ECHO_REPLY)+sizeof(AData);
			char AReply[sizeof(ICMP_ECHO_REPLY)+sizeof(AData)];
			ARetVal = IcmpSendEcho(hIcmp, Addr, &AData, sizeof(AData), NULL,
				AReply, AReplySize, 1000);
			if (ARetVal == 0)
				ARetVal = GetLastError();
			else
				ARetVal = 0;
		}
		__finally {
			IcmpCloseHandle(hIcmp);
		}
	}
	else
		ARetVal = GetLastError();
	return ARetVal;
}

[Delphi 版]

uses iphlpapi, winsock;

const
  ICMPDLL = 'icmp.dll';

type
  PIPOptionInformation = ^TIPOptionInformation;

  TIPOptionInformation = packed record
    TTL: Byte;
    TOS: Byte;
    Flags: Byte;
    OptionsSize: Byte;
    OptionsData: PChar;
  end;

  ICMP_ECHO_REPLY = packed record
    Address: DWORD;
    Status: DWORD;
    RTT: DWORD;
    DataSize: Word;
    Reserved: Word;
    Data: Pointer;
    Options: TIPOptionInformation;
  end;

  TIcmpEchoReply = ICMP_ECHO_REPLY;
  PIcmpEchoReply = ^TIcmpEchoReply;

function IcmpCreateFile: THandle; stdcall; external ICMPDLL;
function IcmpCloseHandle(IcmpHandle: THandle): Boolean; stdcall;
  external ICMPDLL;
function IcmpSendEcho(IcmpHandle: THandle; DestinationAddress: DWORD;
  RequestData: Pointer; RequestSize: Word; RequestOptions: PIPOptionInformation;
  ReplyBuffer: Pointer; ReplySize: DWORD; Timeout: DWORD): DWORD; stdcall;
  external ICMPDLL;

// ---------------------------------------------------------------------------

function Ping(const AHost: AnsiString): Integer;
var
  hIcmp: THandle;
  AData: array [0 .. 31] of Byte;
  AReply: array [0 .. sizeof(ICMP_ECHO_REPLY) + 31] of Byte;
  Addr: Cardinal;
  ent: PHostEnt;
  AReplySize: Integer;
begin
  hIcmp := IcmpCreateFile;
  if hIcmp <> INVALID_HANDLE_VALUE then
  begin
    try
      Addr := inet_addr(PAnsiChar(AHost));
      if Addr = INADDR_NONE then
      begin
        ent := gethostbyname(PAnsiChar(AHost));
        if Assigned(ent) and (ent.h_addrtype = AF_INET) then
          Addr := PCardinal(ent.h_addr_list[0])^
        else
        begin
          Result := GetLastError;
          Exit;
        end;      
      end;
      AReplySize := sizeof(ICMP_ECHO_REPLY) + sizeof(AData);
      Result := IcmpSendEcho(hIcmp, Addr, @AData[0], sizeof(AData), nil,
        @AReply[0], AReplySize, 1000);
      if Result = 0 then
        Result := GetLastError()
      else
        Result := 0;
    finally
      IcmpCloseHandle(hIcmp);
    end;
  end
  else
    Result := GetLastError();
end;

成功,返回0,失败返回错误代码,参数可以是主机名或IP地址。如果是主机名,则会解析成IP地址才能。

注意无论那个版本,都要先调用 WSAStartup,在不需要时调用WSACleanup,这个常识就不说了。

分享到: