WinAPI IcmpSendEcho on 64-bit platform
WinAPI IcmpSendEcho on 64-bit platform
我正在尝试使用 Windows 提供的IcmpSendEcho
函数发送 ping 请求,如此处所述。
在大多数情况下,这似乎很简单,但是,我无法理解调用方提供的回显请求缓冲区(LPVOID ReplyBuffer
和DWORD ReplySize
参数(应该如何设置和使用。文档声明如下:
ReplyBuffer
[...]
用于保存对回显请求的任何回复的缓冲区。返回时,缓冲区包含一组ICMP_ECHO_REPLY结构,后跟回复的选项和数据。缓冲区应足够大,以容纳至少一个ICMP_ECHO_REPLY结构以及 RequestSize 数据字节。
在 64 位平台上,返回时缓冲区包含一个ICMP_ECHO_REPLY32结构数组,后跟回复的选项和数据。
和
ReplySize
[...]
回复缓冲区的分配大小(以字节为单位(。缓冲区应足够大,以容纳至少一个ICMP_ECHO_REPLY结构以及 RequestSize 数据字节。在 64 位平台上,缓冲区应足够大,以容纳至少一个ICMP_ECHO_REPLY32结构以及 RequestSize 字节的数据。
此缓冲区还应足够大,以便再容纳 8 个字节的数据(ICMP 错误消息的大小(。
我正在开发并针对 64 位系统和版本的 Windows 进行开发,因此我从文档中的理解是我需要使用ICMP_ECHO_REPLY32
来计算缓冲区大小,即:
ReplySize >= sizeof(ICMP_ECHO_REPLY32) + RequestSize + 8
但是,在测试此功能时,IcmpSendEcho
总是立即失败,返回值为 0。
对于RequestSize < 4
,GetLastError()
返回ERROR_INVALID_PARAMETER
,文档说这表示">ReplySize参数指定一个小于ICMP_ECHO_REPLY或ICMP_ECHO_REPLY32结构大小的值"。
对于RequestSize >= 4
,GetLastError
返回一个未记录的值,当用GetIpErrorString()
解析时,该值表示"一般失败"。
我还看到文档中的示例代码排除了回复缓冲区中为"ICMP 错误消息"指定的 8 个字节(并且已经看到猜测 8 对于 64 位平台不正确( - 我不确定这是否会影响问题。
如果我改用ICMP_ECHO_REPLY
进行ReplySize
计算,IcmpSendEcho
不再为任何有效负载大小失败,但是似乎仍然不清楚该函数在内部实际使用哪个函数。即,ReplyBuffer
写成ICMP_ECHO_REPLY
数组还是ICMP_ECHO_REPLY32
数组?由于 Once 必须通过强制转换指针类型来访问缓冲区,因此使用错误的类型可能会静默地错位并破坏其上的所有操作。
那么应该如何正确设置回复缓冲区呢?应该使用ICMP_ECHO_REPLY
还是ICMP_ECHO_REPLY32
?
这是我一直在使用的测试代码:
#include <WS2tcpip.h>
#include <Windows.h>
#include <iphlpapi.h>
#include <IcmpAPI.h>
#pragma comment(lib, "Iphlpapi.lib")
#pragma comment(lib, "Ws2_32.lib")
int main()
{
// Create the ICMP context.
HANDLE icmp_handle = IcmpCreateFile();
if (icmp_handle == INVALID_HANDLE_VALUE) {
throw;
}
// Parse the destination IP address.
IN_ADDR dest_ip{};
if (1 != InetPtonA(AF_INET, "<IP here>", &dest_ip)) {
throw;
}
// Payload to send.
constexpr WORD payload_size = 1;
unsigned char payload[payload_size]{};
// Reply buffer for exactly 1 echo reply, payload data, and 8 bytes for ICMP error message.
constexpr DWORD reply_buf_size = sizeof(ICMP_ECHO_REPLY32) + payload_size + 8;
unsigned char reply_buf[reply_buf_size]{};
// Make the echo request.
DWORD reply_count = IcmpSendEcho(icmp_handle, dest_ip.S_un.S_addr,
payload, payload_size, NULL, reply_buf, reply_buf_size, 10000);
// Return value of 0 indicates failure, try to get error info.
if (reply_count == 0) {
auto e = GetLastError();
// Some documented error codes from IcmpSendEcho docs.
switch (e) {
case ERROR_INSUFFICIENT_BUFFER:
throw;
case ERROR_INVALID_PARAMETER:
throw;
case ERROR_NOT_ENOUGH_MEMORY:
throw;
case ERROR_NOT_SUPPORTED:
throw;
case IP_BUF_TOO_SMALL:
throw;
}
// Try to get an error message for all other error codes.
DWORD buf_size = 1000;
WCHAR buf[1000];
GetIpErrorString(e, buf, &buf_size);
throw;
}
// Close ICMP context.
IcmpCloseHandle(icmp_handle);
}
我认为文档是错误的。
如果你看一下ICMP_ECHO_REPLY32中的实际内容,你会发现一些用__ptr32
属性声明的指针,这里有一些关于这些人的信息。
然而,当你真正取消引用其中一个时,它毕竟是一个真正的 64 位指针,这不是 Hans 帖子中所说的。 我想自从他写了这篇文章以来,情况已经发生了变化(我刚刚在Visual Studio 2017上测试过(。
无论如何,去想一想,但是破坏您的代码的是,即使在 64 位构建中,此类指针的sizeof
也会返回4,这意味着ICMP_ECHO_REPLY32
结构的总报告大小不够大(有效负载如此之小(甚至无法容纳单个 ICMP 回复,因此IcmpSendEcho
因ERROR_INVALID_PARAMETER
而失败, 正如你所观察到的。
因此,要使代码正常工作,您所要做的就是在设置请求时使用sizeof (ICMP_ECHO_REPLY)
,然后将reply_buf
转换为const ICMP_ECHO_REPLY *
以解释结果。
这是我的(工作(测试程序的整个代码,添加了一些日志记录(为了简洁起见,我删除了一些错误处理(:
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <WS2tcpip.h>
#include <Windows.h>
#include <iphlpapi.h>
#include <IcmpAPI.h>
#include <iostream>
#pragma comment(lib, "Iphlpapi.lib")
#pragma comment(lib, "Ws2_32.lib")
int main()
{
// Create the ICMP context.
HANDLE icmp_handle = IcmpCreateFile();
if (icmp_handle == INVALID_HANDLE_VALUE) {
throw;
}
// Parse the destination IP address.
IN_ADDR dest_ip{};
if (1 != InetPtonA(AF_INET, "89.238.162.170", &dest_ip)) {
throw;
}
// Payload to send.
constexpr WORD payload_size = 1;
unsigned char payload[payload_size] { 42 };
// Reply buffer for exactly 1 echo reply, payload data, and 8 bytes for ICMP error message.
constexpr DWORD reply_buf_size = sizeof(ICMP_ECHO_REPLY) + payload_size + 8;
unsigned char reply_buf[reply_buf_size]{};
// Make the echo request.
DWORD reply_count = IcmpSendEcho(icmp_handle, dest_ip.S_un.S_addr,
payload, payload_size, NULL, reply_buf, reply_buf_size, 10000);
// Return value of 0 indicates failure, try to get error info.
if (reply_count == 0) {
auto e = GetLastError();
DWORD buf_size = 1000;
WCHAR buf[1000];
GetIpErrorString(e, buf, &buf_size);
std::cout << "IcmpSendEcho returned error " << e << " (" << buf << ")" << std::endl;
return 255;
}
const ICMP_ECHO_REPLY *r = (const ICMP_ECHO_REPLY *) reply_buf;
struct in_addr addr;
addr.s_addr = r->Address;
char *s_ip = inet_ntoa (addr);
std::cout << "Reply from: " << s_ip << ": bytes=" << r->DataSize << " time=" << r->RoundTripTime << "ms TTL=" << (int) r->Options.Ttl << std::endl;
// Close ICMP context.
IcmpCloseHandle(icmp_handle);
return 0;
}
在rextester上运行它。(这是我网站的IP地址,你看到我在那里做了什么吗?:)
未来访客注意:此代码不会检查IcmpSendEcho
返回时r->Status
。 但它应该这样做。 0 = 成功,请参阅文档。
- 芬威克树(BIT).找到具有给定累积频率的最小索引,单位为 O(logN)
- 将应用程序从32位移植到64位时出现问题
- 64位机器上的C++内存对齐
- qmake:检测目标位宽(32 位或 64 位)
- 如何在 64 位 vb.net Windows 应用程序中引用 32 位 dll
- Qt 5.11.2 (Clang 8.0 (Apple), 64 位), 找不到 QJSEngine 文件
- Mingw-64 在构建和安装后不会编译 openCV 代码
- 提升 1.64 单元测试编译失败
- 在 64 位边界上对齐C++结构数组?
- 如何为字符串生成唯一但一致的 N 位哈希(小于 64 位)?
- WinAPI IcmpSendEcho on 64-bit platform
- 仅在测试中"dlopen failed: is 32-bit instead of 64-bit"
- CMake 64-bit with SFML 64-bit
- boost 1.69 visual studio 2017 64-bit
- gtkmm 3 windows 64 bit
- C++ Proxy DLL (64-Bit)
- Linking LAPACK, 64-bit, Visual Studio 2013
- Quantlib 64-bit for C#?
- 64 bit addresses
- Directx 11.0 with visual studio 2012 on windows 8 64 bit