需在程式中快速檢測遠端主機是否在線?無需 Indy、ICS 等第三方元件,Windows 核心 icmp.dll 內建 IcmpCreateFile、IcmpSendEcho、IcmpCloseHandle 三大 API,動態載入即可實現標準 ICMP Echo Request/Reply,支援 IPv4、自定義封包大小、TTL、逾時。本文提供完整 Delphi 封裝代碼與結構定義。
type
PIPOptionInfo = ^TIPOptionInfo;
TIPOptionInfo = packed record
TTL: Byte; // Time To Live
TOS: Byte; // Type of Service
Flags: Byte;
OptionsSize: Byte;
OptionsData: PChar;
end;
PIcmpEchoReply = ^TIcmpEchoReply;
TIcmpEchoReply = packed record
Address: DWORD; // 回應者 IP
Status: DWORD; // IP_SUCCESS=0 成功
RTT: DWORD; // 往返延遲 (毫秒)
DataSize: Word; // 回應資料長度
Reserved: Word;
Data: Pointer; // 回應資料指標
Options: TIPOptionInfo;
end;
type
TIcmpCreateFile = function: THandle; stdcall;
TIcmpCloseHandle = function(h: THandle): Boolean; stdcall;
TIcmpSendEcho = function(
h: THandle; DestAddr: DWORD; ReqData: Pointer; ReqSize: Word;
ReqOpt: PIPOptionInfo; ReplyBuf: Pointer; ReplySize: DWORD; Timeout: DWORD
): DWORD; stdcall;
var
hIcmpDll: THandle;
IcmpCreateFile: TIcmpCreateFile;
IcmpCloseHandle: TIcmpCloseHandle;
IcmpSendEcho: TIcmpSendEcho;
procedure CheckHostOnline(DestIP: String);
var
IPOpt: TIPOptionInfo;
IPAddr: DWORD;
ReqData, RepData: PChar;
RepBuf: PIcmpEchoReply;
BufSize, Timeout: DWORD;
I: Integer;
Msg: String;
begin
// 1. 載入 icmp.dll
hIcmpDll := LoadLibrary('icmp.dll');
if hIcmpDll = 0 then Exit;
// 2. 取得函數位址
@IcmpCreateFile := GetProcAddress(hIcmpDll, 'IcmpCreateFile');
@IcmpCloseHandle := GetProcAddress(hIcmpDll, 'IcmpCloseHandle');
@IcmpSendEcho := GetProcAddress(hIcmpDll, 'IcmpSendEcho');
if (@IcmpCreateFile = nil) or (@IcmpSendEcho = nil) then begin
FreeLibrary(hIcmpDll); Exit;
end;
// 3. 初始化 Winsock (舊版需呼叫)
WSAStartup($101, WSAData);
// 4. 建立 ICMP 句柄
hIcmp := IcmpCreateFile;
// 5. 解析目標 IP
IPAddr := inet_addr(PChar(DestIP)); // 需 Winsock 單元
// 6. 準備請求/回應緩衝區
Msg := 'Hello, Ping!';
ReqData := PChar(Msg);
GetMem(RepData, 64);
BufSize := SizeOf(TIcmpEchoReply) + 64;
GetMem(RepBuf, BufSize);
FillChar(RepBuf^, BufSize, 0);
RepBuf.Data := RepData;
// 7. 設定 IP 選項
FillChar(IPOpt, SizeOf(IPOpt), 0);
IPOpt.TTL := 64;
Timeout := 1000; // 1 秒逾時
// 8. 發送 Echo Request
I := IcmpSendEcho(hIcmp, IPAddr, ReqData, Length(Msg), @IPOpt, RepBuf, BufSize, Timeout);
// 9. 判斷結果
if I > 0 then begin
if RepBuf.Status = 0 then // IP_SUCCESS
ShowMessage(Format('主機 %s 回應正常,RTT=%d ms', [DestIP, RepBuf.RTT]))
else
ShowMessage(Format('主機 %s 回應錯誤,狀態碼=%d', [DestIP, RepBuf.Status]));
end else
ShowMessage(Format('主機 %s 無回應 (逾時或不可達)', [DestIP]));
// 10. 清理資源
FreeMem(RepData); FreeMem(RepBuf);
IcmpCloseHandle(hIcmp);
FreeLibrary(hIcmpDll);
WSACleanup;
end;
| 參數 | 建議值 | 說明 |
|---|---|---|
| TTL | 64~128 | 跨路由器需足夠 TTL,本地網 32 即可 |
| Timeout | 500~3000 ms | 視網路延遲調整,跨國建議 3000+ |
| Request Data Size | 32~64 bytes | 過大可能被防火牆丟棄 |
| Reply Buffer | SizeOf(TIcmpEchoReply) + MaxReplyData | 必須足夠大否則資料截斷 |
| 方案 | 優點 | 缺點 |
|---|---|---|
| icmp.dll 動態載入 | 零依賴、系統內建、支援原始 Socket 權限不足環境、極輕量 | 需動態載入代碼、IPv6 不支援、錯誤碼需查表 |
| TIdICMPClient (Indy) | 封裝完善、事件驅動、支援 IPv6、跨平台 | 引入龐大 Indy 元件、需管理員權限建立 Raw Socket |
| ShellExecute ping.exe | 最簡單 | 需解析文字輸出、無法獲取精確 RTT、有視窗閃爍 |
掌握 icmp.dll 動態呼叫,讓你的 Delphi 程式擁有「系統級網路診斷能力」,無需任何第三方依賴。