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”)。
IcmpSendEcho
的 MSDN documentation 并没有说明这一点,因此微软可能已经从 Windows XP 中的版本更改了此方法的内部实现,以便非零Timeout
现在需要。
我们有一个遗留 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”)。
IcmpSendEcho
的 MSDN documentation 并没有说明这一点,因此微软可能已经从 Windows XP 中的版本更改了此方法的内部实现,以便非零Timeout
现在需要。