スキップしてメイン コンテンツへ

 none
64bit環境下でIcmpSendEchoに渡すReplyBufferのサイズについて RRS feed

  • 質問

  • 現在Win32 APIのIcmpSendEcho (http://msdn.microsoft.com/ja-jp/library/windows/desktop/aa366050%28v=vs.85%29.aspx)を使用して、
    他のホストにping要求を送信するプログラムを作成しております。

    ドキュメントを見ると、64bitでコンパイルする場合、ReplyBufferにはsizeof(ICMP_ECHO_REPLY32)+リクエストデータサイズ+8を確保しろとあります。

    しかし、実際はドキュメント通り指定すると、対象ホストがネットワークに接続しているにも関わらず、戻り値が0で、GetLastErrorの値が11050で返るようです。

    ReplyBufferにsizeof(ICMP_ECHO_REPLY)+リクエストデータサイズ+8とすると、想定通り成功し、ICMP_ECHO_REPLY.Statusにも0が入るようです。

    また、ICMP_ECHO_REPLY32を使う場合でも、更に4バイト余分に確保すれば、レスポンスは返ってくるようです。
    しかし、その場合でも、VisualStudio上で確認すると、ICMP_ECHO_REPLY32.Options.OptionsDataに不正なポインタ値が格納されているようです。私のデバッグ環境では、0x40が格納されていました。

    実際はリプライバッファーにどの位のバッファを確保すればいいのか、また、どのような値を期待すればいいのか、ご教示お願いできますでしょうか?

    2014年7月29日 11:04

回答

  • XXX32という命名の構造体や、そのフィールドに使われているPOINTER_32マクロ(__ptr32修飾子)は、64bit環境において64bitネイティブプロセスが32bitのWOW64プロセスまたは32bitドライバー(64bit環境での32bitドライバーは一般的ではありませんが)と直接通信するために用意されたバージョンだと思います。おそらくMSDNライブラリの説明中の「64-bit platform」というのもそのことを指しているのだと思われます。

    一般的なコンパイラ エラー (Windows)

    IcmpSendEcho function (Windows)

    #include <cstdio>
    #include <vector>
    #include <winsock2.h>
    #include <iphlpapi.h>
    #include <icmpapi.h>
    #include <conio.h>
    
    #pragma comment(lib, "iphlpapi.lib")
    #pragma comment(lib, "ws2_32.lib")
    
    int main()
    {
    	auto hIcmp = ::IcmpCreateFile();
    	if (hIcmp == INVALID_HANDLE_VALUE)
    	{
    		printf("Failed to create ICMP stream handle.");
    		return -1;
    	}
    
    	{
    		printf("sizeof(void*) = %Iu\n", sizeof(void*));
    		printf("sizeof(void* __ptr64) = %Iu\n", sizeof(void* __ptr64)); // void* POINTER_64
    		printf("sizeof(void* __ptr32) = %Iu\n", sizeof(void* __ptr32)); // void* POINTER_32
    
    		printf("sizeof(ICMP_ECHO_REPLY) = %Iu\n", sizeof(ICMP_ECHO_REPLY));
    		printf("sizeof(IP_OPTION_INFORMATION) = %Iu\n", sizeof(IP_OPTION_INFORMATION));
    #ifdef _WIN64
    		printf("sizeof(ICMP_ECHO_REPLY32) = %Iu\n", sizeof(ICMP_ECHO_REPLY32));
    		printf("sizeof(IP_OPTION_INFORMATION32) = %Iu\n", sizeof(IP_OPTION_INFORMATION32));
    #endif
    
    		printf("ERROR_NOT_ENOUGH_MEMORY = %lu\n", ERROR_NOT_ENOUGH_MEMORY);
    		printf("ERROR_INVALID_PARAMETER = %lu\n", ERROR_INVALID_PARAMETER);
    		printf("ERROR_INSUFFICIENT_BUFFER = %lu\n", ERROR_INSUFFICIENT_BUFFER);
    		printf("ERROR_NOT_SUPPORTED = %lu\n", ERROR_NOT_SUPPORTED);
    		printf("IP_BUF_TOO_SMALL = %lu\n", IP_BUF_TOO_SMALL);
    		printf("IP_GENERAL_FAILURE = %lu\n", IP_GENERAL_FAILURE);
    	}
    
    	const wchar_t SendingData[] = L"My ICMP sending data";
    	const DWORD requestBufLen = sizeof(SendingData);
    	const DWORD icmpErrorMessageBufferSize = 8;
    	const DWORD replyBufLen = sizeof(ICMP_ECHO_REPLY) + sizeof(SendingData) + icmpErrorMessageBufferSize;
    	std::vector<byte> requestBuffer(requestBufLen);
    	std::vector<byte> replyBuffer(replyBufLen);
    	memcpy_s(&requestBuffer[0], requestBuffer.size(), SendingData, sizeof(SendingData));
    
    	printf("\n");
    	printf("Sending...\n");
    	const DWORD timeoutValMillisec = 1000;
    	const auto dwRetVal = ::IcmpSendEcho(hIcmp,
    		inet_addr("127.0.0.1"),
    		&requestBuffer[0], requestBufLen,
    		nullptr,
    		&replyBuffer[0], replyBufLen,
    		timeoutValMillisec);
    	if (dwRetVal != 0)
    	{
    		auto pIcmpEchoReply = reinterpret_cast<const ICMP_ECHO_REPLY*>(&replyBuffer[0]);
    		printf("Received %lu messages.\n", dwRetVal);
    		printf("\n");
    		printf("Status: %lu\n", pIcmpEchoReply->Status);
    		printf("RoundTripTime: %lu\n", pIcmpEchoReply->RoundTripTime);
    		printf("DataSize: %hu\n", pIcmpEchoReply->DataSize);
    		printf("Data: 0x%p\n", pIcmpEchoReply->Data);
    		auto receivedMessage = static_cast<const wchar_t*>(pIcmpEchoReply->Data);
    		wprintf(L"Messasge: %s\n", receivedMessage);
    	}
    	else
    	{
    		const auto lastError = ::GetLastError();
    		printf("Failed to call IcmpSendEcho().\n");
    		printf("Error: %lu\n", lastError);
    	}
    
    	::IcmpCloseHandle(hIcmp);
    	hIcmp = nullptr;
    
    	puts("Press any...");
    	_getch();
    
    	return 0;
    }

    状況や要求仕様にも依存しますが、通常のアプリケーションプログラムにおいて、32bit版と64bit版とでコードを分けなければいけないようなことはまずない(普通はWin32 API側で吸収してくれる)はずなので、64bit版でもICMP_ECHO_REPLYやIP_OPTION_INFORMATIONを使うのが妥当ではないでしょうか。

    • 編集済み sygh 2014年8月9日 15:37
    • 回答としてマーク yusuke.ito 2014年8月11日 7:17
    2014年8月9日 15:30

すべての返信

  • XXX32という命名の構造体や、そのフィールドに使われているPOINTER_32マクロ(__ptr32修飾子)は、64bit環境において64bitネイティブプロセスが32bitのWOW64プロセスまたは32bitドライバー(64bit環境での32bitドライバーは一般的ではありませんが)と直接通信するために用意されたバージョンだと思います。おそらくMSDNライブラリの説明中の「64-bit platform」というのもそのことを指しているのだと思われます。

    一般的なコンパイラ エラー (Windows)

    IcmpSendEcho function (Windows)

    #include <cstdio>
    #include <vector>
    #include <winsock2.h>
    #include <iphlpapi.h>
    #include <icmpapi.h>
    #include <conio.h>
    
    #pragma comment(lib, "iphlpapi.lib")
    #pragma comment(lib, "ws2_32.lib")
    
    int main()
    {
    	auto hIcmp = ::IcmpCreateFile();
    	if (hIcmp == INVALID_HANDLE_VALUE)
    	{
    		printf("Failed to create ICMP stream handle.");
    		return -1;
    	}
    
    	{
    		printf("sizeof(void*) = %Iu\n", sizeof(void*));
    		printf("sizeof(void* __ptr64) = %Iu\n", sizeof(void* __ptr64)); // void* POINTER_64
    		printf("sizeof(void* __ptr32) = %Iu\n", sizeof(void* __ptr32)); // void* POINTER_32
    
    		printf("sizeof(ICMP_ECHO_REPLY) = %Iu\n", sizeof(ICMP_ECHO_REPLY));
    		printf("sizeof(IP_OPTION_INFORMATION) = %Iu\n", sizeof(IP_OPTION_INFORMATION));
    #ifdef _WIN64
    		printf("sizeof(ICMP_ECHO_REPLY32) = %Iu\n", sizeof(ICMP_ECHO_REPLY32));
    		printf("sizeof(IP_OPTION_INFORMATION32) = %Iu\n", sizeof(IP_OPTION_INFORMATION32));
    #endif
    
    		printf("ERROR_NOT_ENOUGH_MEMORY = %lu\n", ERROR_NOT_ENOUGH_MEMORY);
    		printf("ERROR_INVALID_PARAMETER = %lu\n", ERROR_INVALID_PARAMETER);
    		printf("ERROR_INSUFFICIENT_BUFFER = %lu\n", ERROR_INSUFFICIENT_BUFFER);
    		printf("ERROR_NOT_SUPPORTED = %lu\n", ERROR_NOT_SUPPORTED);
    		printf("IP_BUF_TOO_SMALL = %lu\n", IP_BUF_TOO_SMALL);
    		printf("IP_GENERAL_FAILURE = %lu\n", IP_GENERAL_FAILURE);
    	}
    
    	const wchar_t SendingData[] = L"My ICMP sending data";
    	const DWORD requestBufLen = sizeof(SendingData);
    	const DWORD icmpErrorMessageBufferSize = 8;
    	const DWORD replyBufLen = sizeof(ICMP_ECHO_REPLY) + sizeof(SendingData) + icmpErrorMessageBufferSize;
    	std::vector<byte> requestBuffer(requestBufLen);
    	std::vector<byte> replyBuffer(replyBufLen);
    	memcpy_s(&requestBuffer[0], requestBuffer.size(), SendingData, sizeof(SendingData));
    
    	printf("\n");
    	printf("Sending...\n");
    	const DWORD timeoutValMillisec = 1000;
    	const auto dwRetVal = ::IcmpSendEcho(hIcmp,
    		inet_addr("127.0.0.1"),
    		&requestBuffer[0], requestBufLen,
    		nullptr,
    		&replyBuffer[0], replyBufLen,
    		timeoutValMillisec);
    	if (dwRetVal != 0)
    	{
    		auto pIcmpEchoReply = reinterpret_cast<const ICMP_ECHO_REPLY*>(&replyBuffer[0]);
    		printf("Received %lu messages.\n", dwRetVal);
    		printf("\n");
    		printf("Status: %lu\n", pIcmpEchoReply->Status);
    		printf("RoundTripTime: %lu\n", pIcmpEchoReply->RoundTripTime);
    		printf("DataSize: %hu\n", pIcmpEchoReply->DataSize);
    		printf("Data: 0x%p\n", pIcmpEchoReply->Data);
    		auto receivedMessage = static_cast<const wchar_t*>(pIcmpEchoReply->Data);
    		wprintf(L"Messasge: %s\n", receivedMessage);
    	}
    	else
    	{
    		const auto lastError = ::GetLastError();
    		printf("Failed to call IcmpSendEcho().\n");
    		printf("Error: %lu\n", lastError);
    	}
    
    	::IcmpCloseHandle(hIcmp);
    	hIcmp = nullptr;
    
    	puts("Press any...");
    	_getch();
    
    	return 0;
    }

    状況や要求仕様にも依存しますが、通常のアプリケーションプログラムにおいて、32bit版と64bit版とでコードを分けなければいけないようなことはまずない(普通はWin32 API側で吸収してくれる)はずなので、64bit版でもICMP_ECHO_REPLYやIP_OPTION_INFORMATIONを使うのが妥当ではないでしょうか。

    • 編集済み sygh 2014年8月9日 15:37
    • 回答としてマーク yusuke.ito 2014年8月11日 7:17
    2014年8月9日 15:30
  • 確かに*32を使わずに、そのまま使用した方がこちらの想定通りの動作をしたので、32/64で同じコードを使用することにします。

    回答ありがとうございました。

    2014年8月11日 7:17