Windows 無內建 ping API(直到 Winsock 2.0 仍無),必須直接呼叫 ICMP.dll (ICMPAPI.H/LIB) 發送 ICMP Echo Request 並接收 Echo Reply。本文介紹一個封裝 CSocket 的 CPing 類,提供主機名/IP 解析、Ping 發送、回應時間測量功能。
從 Microsoft SDK / Platform SDK 獲取並加入專案:
ICMPAPI.H、ICMPAPI.CPP、ICMP.LIBIPEXPORT.H、IPEXPORT.CPP(IP 標頭定義)
class CPing : public CSocket {
public:
// 解析 IP 字串 -> ULONG (網路位元組序)
unsigned long ResolveIP(CString strIP);
// 解析主機名 -> ULONG (經 DNS/WINS)
unsigned long ResolveName(CString strHostName);
// ULONG IP -> 點分十進制字串
CString GetIP(unsigned long ulIP);
// 核心:Ping 指定 IP,回傳往返時間 (ms),失敗回傳 -1
DWORD PingHost(unsigned long ulIP, int iPingTimeout = 2000);
};
DWORD CPing::PingHost(unsigned long ulIP, int iPingTimeout) {
HANDLE hIcmp = IcmpCreateFile();
if (hIcmp == INVALID_HANDLE_VALUE) return (DWORD)-1;
// 準備發送緩衝區 (任意資料,如 "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
char sendBuf[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
IP_OPTION_INFORMATION ipOpt = {0};
ipOpt.Ttl = 128; // Time To Live
// 接收緩衝區:ICMP_ECHO_REPLY + 回應資料
DWORD replySize = sizeof(ICMP_ECHO_REPLY) + sizeof(sendBuf);
LPVOID replyBuf = malloc(replySize);
DWORD dwRet = IcmpSendEcho(
hIcmp,
ulIP, // 目標 IP (網路序)
sendBuf, sizeof(sendBuf), // 發送資料
&ipOpt, // IP 選項
replyBuf, replySize, // 接收緩衝區
iPingTimeout // 逾時 (ms)
);
DWORD rtt = (DWORD)-1;
if (dwRet > 0) {
PICMP_ECHO_REPLY pReply = (PICMP_ECHO_REPLY)replyBuf;
if (pReply->Status == IP_SUCCESS) {
rtt = pReply->RoundTripTime; // 毫秒
}
}
free(replyBuf);
IcmpCloseHandle(hIcmp);
return rtt;
}
unsigned long CPing::ResolveIP(CString strIP) {
return inet_addr(strIP); // 點分十進制 -> ULONG (網路序)
}
unsigned long CPing::ResolveName(CString strHostName) {
struct hostent *pHost = gethostbyname(strHostName);
if (!pHost) return INADDR_NONE;
return *(unsigned long*)pHost->h_addr_list[0]; // 已是網路序
}
CString CPing::GetIP(unsigned long ulIP) {
struct in_addr addr; addr.S_un.S_addr = ulIP;
return CString(inet_ntoa(addr)); // 網路序 -> 點分十進制
}
CPing ping;
CString host = _T("www.google.com");
DWORD ip = ping.ResolveName(host);
if (ip != INADDR_NONE) {
CString ipStr = ping.GetIP(ip);
DWORD rtt = ping.PingHost(ip, 3000);
if (rtt != (DWORD)-1)
AfxMessageBox(_T("Ping ") + ipStr + _T(" 成功,RTT = ") + IntToStr(rtt) + _T(" ms"));
else
AfxMessageBox(_T("Ping 失敗或逾時"));
} else {
AfxMessageBox(_T("無法解析主機名"));
}
IcmpCreateFile 需管理員權限(Vista+ UAC),普通用戶可能失敗。解決:Manifest 要求 requireAdministrator 或使用 IcmpSendEcho2Ex (非阻塞) 搭配原始 Socket 實現(複雜度高)。IcmpSendEcho2 (非同步) + 完成埠/事件通知。Icmp6CreateFile/Icmp6SendEcho,結構體不同。封裝 ICMP.dll 實現 Ping 是 Windows 網路程式設計的經典課題。本文提供的 CPing 類涵蓋解析、發送、接收、計時完整流程,可直接整合至專案。現代開發若需跨平台,建議改用 boost::asio (ICMP) 或系統 ping 命令解析輸出。