Windows CE 環境での ICMP Echo (Ping) 機能の実装方法

Windows CE などの組み込みオペレーティングシステムにおいて、標準コマンドラインが利用できない場合でもネットワーク接続性を確認するために、ICMP Echo 要求(Ping)を API レベルで実装する必要があります。以下に、Winsock と IP Helper API を利用した実装例を示します。

必須ヘッダーとリンクライブラリ

ICMP 関数とネットワークソケットを利用するために、以下のファイルをインクルードし、ビルド設定で対応する静的ライブラリをリンク対象に追加します。

#include <winsock2.h>
#include <iphlpapi.h>
#include <windows.h>
#include <tchar.h>

// ビルド環境に応じてライブラリを追加
#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "ws2.lib")

ICMP Ping 関数の実装

ホスト名の解決(DNS 順引き・逆引き)と ICMP Echo 要求の送信を統合した関数を定義します。可読性とエラーハンドリングを強化するため、制御フローと変数名は再構成されています。

int ExecuteIcmpPing(const TCHAR* targetHost, int timeToLive, int timeoutMs, 
                    TCHAR* resolvedName, int* roundTripTimeMs) 
{
    if (!targetHost || !resolvedName || !roundTripTimeMs) return -1;

    *resolvedName = _T('\0');
    *roundTripTimeMs = 0;

    // Unicode から ANSI への変換(WinCE 環境用)
    char asciiHost[256] = { 0 };
    WideCharToMultiByte(CP_ACP, 0, targetHost, -1, asciiHost, sizeof(asciiHost), NULL, NULL);

    struct in_addr destAddr;
    destAddr.S_un.S_addr = inet_addr(asciiHost);
    DWORD targetIp = destAddr.S_un.S_addr;

    struct hostent* hostEntry = NULL;

    // IP アドレス形式でなければ DNS 逆引きまたは順引きを試行
    if (targetIp == INADDR_NONE) {
        hostEntry = gethostbyname(asciiHost);
        if (hostEntry) {
            targetIp = *(DWORD*)(*hostEntry->h_addr_list);
            char* ipStr = inet_ntoa(*((struct in_addr*)&targetIp));
            if (ipStr) {
                MultiByteToWideChar(CP_ACP, 0, ipStr, -1, resolvedName, MAX_PATH);
            }
        }
    } else {
        hostEntry = gethostbyaddr((const char*)&destAddr, sizeof(struct in_addr), AF_INET);
        if (hostEntry) {
            MultiByteToWideChar(CP_ACP, 0, hostEntry->h_name, -1, resolvedName, MAX_PATH);
        }
    }

    if (targetIp == INADDR_NONE) {
        return -1; // ホスト解決失敗
    }

    // ICMP ハンドルの作成
    HANDLE icmpHandle = IcmpCreateFile();
    if (icmpHandle == INVALID_HANDLE_VALUE) {
        return -2; // ハンドル作成失敗
    }

    IP_OPTION_INFORMATION options = { 0 };
    options.Ttl = timeToLive;
    options.Tos = 0;
    options.Flags = 0;
    options.OptionsSize = 0;
    options.OptionsData = NULL;

    ICMP_ECHO_REPLY echoReply = { 0 };
    int status = 0;

    // Echo 要求の送信
    if (IcmpSendEcho(icmpHandle, targetIp, NULL, 0, &options, 
                     &echoReply, sizeof(ICMP_ECHO_REPLY), timeoutMs)) {
        if (echoReply.Status == IP_SUCCESS) {
            status = 1; // 成功
            *roundTripTimeMs = echoReply.RoundTripTime;
        } else {
            status = -3; // タイムアウトまたは接続拒否
        }
    }

    IcmpCloseHandle(icmpHandle);
    return status;
}

呼び出し例と結果処理

上記の関数は、ターゲットの IP またはドメイン名、TTL、タイムアウト時間を指定して呼び出します。戻り値に基づいて接続状況をログ出力します。

void VerifyNetworkReachability() 
{
    static const TCHAR* endpointList[] = {
        _T("202.96.209.5"),   // 例: 外部 DNS
        _T("220.181.6.18"),   // 例: 検索サービス
        _T("202.100.4.15")    // 例: 地域 DNS
    };

    int targetIndex = 0;
    TCHAR nameBuffer[MAX_PATH] = { 0 };
    int latencyMs = 0;

    int result = ExecuteIcmpPing(endpointList[targetIndex], 64, 3000, nameBuffer, &latencyMs);

    if (result == 1) {
        RETAILMSG(1, (TEXT("[NETWORK] Ping successful to %s. RTT: %d ms\r\n"), 
                      endpointList[targetIndex], latencyMs));
    } else {
        RETAILMSG(1, (TEXT("[NETWORK] Ping failed for %s. Code: %d\r\n"), 
                      endpointList[targetIndex], result));
    }
}

組み込みプロジェクトでは、ネットワークサブシステムの初期化(WSAStartup など)が完了した段階でこの関数を呼び出すよう設計してください。戻り値のステータスコードは、接続確認ロジックや自動再接続処理の分岐条件として利用できます。

タグ: Windows CE ICMP WinSock C言語 組み込みネットワーク

5月27日 03:37 投稿