IcmpSendEcho (ping) 在 Windows 10 上失败

IcmpSendEcho (ping) fails on Windows 10

我们有一个遗留 Delphi 应用程序,它使用 IcmpSendEcho(来自 iphlpapi.dll)来执行回应请求。据我了解,这与命令提示符下的 "ping" 执行相同的功能。

在 Windows XP 上,下面的代码工作正常。当 IPv4 地址正确时,响应很快,如果不正确,将按预期返回错误代码 11010 (IP_REQ_TIMED_OUT)。

然而,在我的 32 位 Windows10 机器上,错误代码是 87 (ERROR_INVALID_PARAMETER)。我仔细查看了 Microsoft documentation for IcmpSendEcho,没有发现任何明显的错误。

"ping 200.1.2.121"(我在代码示例中使用的示例 IPv4 地址)在 XP 和 10 的命令提示符下都按预期工作。

type
  PIpAddress = ^TIpAddress;
  TIpAddress = record
    case Integer of
        0: (S_un_b: TSunB);
        1: (S_un_w: TSunW);
        2: (S_addr: LongWord);
  end;
  IpAddress = TIpAddress;

// Functions imported from external DLLs
function IcmpCreateFile() : THandle; stdcall; external 'iphlpapi.dll';
function IcmpCloseHandle(icmpHandle: THandle) : Boolean; stdcall; external 'iphlpapi.dll';
function IcmpSendEcho(IcmpHandle: THandle; ipDest: IpAddress;
    pRequestData: Pointer; nRequestSize: SmallInt; RequestOptions: Pointer;
    pReplyBuffer: Pointer; dwReplySize: DWORD; dwTimeout: DWORD) : DWORD; stdcall; external 'iphlpapi.dll';

procedure TranslateStringToIpAddress(strIP: String; var ipAddress);
var
    phe: PHostEnt;
    pac: PChar;
begin
    try
        phe := GetHostByName(PChar(strIP));
        if (Assigned(phe)) then
            begin
            pac := phe^.h_addr_list^;
            if (Assigned(pac)) then
                begin
                with TIpAddress(ipAddress).S_un_b do
                    begin
                    by1 := Byte(pac[0]);
                    by2 := Byte(pac[1]);
                    by3 := Byte(pac[2]);
                    by4 := Byte(pac[3]);
                    end;
                end
            else
                begin
                raise Exception.Create('Error getting IP from HostName');
                end;
            end
        else
            begin
            raise Exception.Create('Error getting HostName');
            end;
    except
        FillChar(ipAddress, SizeOf(ipAddress), #0);
    end;
end;

function Ping(strIpAddress : String) : Boolean;
const
    ICMP_ECHO_BUFFER = 128;     // Works as low as 28 on Windows XP (nothing works on Windows 10)
var
    address: IpAddress;
    dwReplies: DWORD;
    {$IFDEF DBG} dwErrorCode: DWORD; {$ENDIF}
    abyReplyBuffer: array[1..ICMP_ECHO_BUFFER] of BYTE;
begin
    // Use this function to determine if an IPv4 address can be reached
    Result := False;

    // "m_cache.hPingHandle" is generated earlier with a call to "IcmpCreateFile"
    if (m_cache.hPingHandle = INVALID_HANDLE_VALUE) then
        Exit;

    TranslateStringToIpAddress(strIpAddress, address);
    dwReplies := IcmpSendEcho(
        m_cache.hPingHandle, address, nil, 0, nil, @abyReplyBuffer, ICMP_ECHO_BUFFER, 0);

    {$IFDEF DBG}
    if (dwReplies = 0) then
        begin
        dwErrorCode := GetLastError();
        // dwErrorCode = 87 (ERROR_INVALID_PARAMETER, "The parameter is incorrect")
        Application.MessageBox(
            PAnsiChar(Format('WinError = %d', [dwErrorCode])), 'Ping failed', MB_ICONEXCLAMATION);
        end;
    {$ENDIF}

    // Success?
    Result := (dwReplies <> 0);
end;

// Usage elsewhere in the application...
Ping('200.1.2.121');    // Works on Windows XP, but fails on Windows 10

根据@FredS 的评论(谢谢!),答案只是让 IcmpSendEcho 的最后一个参数不为零(例如“200”)。

IcmpSendEchoMSDN documentation 并没有说明这一点,因此微软可能已经从 Windows XP 中的版本更改了此方法的内部实现,以便非零Timeout 现在需要。