Я приспособил этот пример кода из MSDN здесь. Он отлично работает, если я создаю его самостоятельно, но если я добавлю его в свое приложение, это не сработает. Для удобства я включаю точную адаптацию класса, который у меня есть.
Я только хочу сделать простой пинг каждую секунду. Raw Sockets не работают без прав администратора, и этот подход ICMP.dll не работает в моем приложении, и я не понимаю, почему. Ошибка 183 идет по строкам "Невозможно создать файл, потому что он уже существует", что не имеет смысла в этом контексте.
Может ли кто-нибудь обнаружить проблему? Большое спасибо за Вашу помощь.
Ping.h
#pragma once
#include "Ping.h"
#include "LogBase.h"
class WinPing :
public Ping
{
public:
WinPing(char* address, int period);
~WinPing();
unsigned long GetRTT();
private:
HANDLE _hIcmpFile;
unsigned long _ipaddr;
DWORD _dwRetVal;
LPVOID _replyBuffer;
DWORD _replySize;
static HANDLE _inputTimer;
int _period;
unsigned long _lastRTT;
static DWORD WINAPI AsyncPingHandler(void* Param);
void DoPing();
};
WinPing.h
#pragma once
#include "Ping.h"
#include "LogBase.h"
class WinPing :
public Ping
{
public:
WinPing(char* address, int period);
~WinPing();
unsigned long GetRTT();
private:
HANDLE _hIcmpFile;
unsigned long _ipaddr;
DWORD _dwRetVal;
LPVOID _replyBuffer;
DWORD _replySize;
static HANDLE _inputTimer;
int _period;
unsigned long _lastRTT;
static DWORD WINAPI AsyncPingHandler(void* Param);
void DoPing();
};
WinPing.cpp
#include "WinPing.h"
#include <winsock2.h>
#include <iphlpapi.h>
#include <icmpapi.h>
#include <stdio.h>
#include "utils.h"
#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "ws2_32.lib")
HANDLE WinPing::_inputTimer = NULL;
char SendData[32] = "Data Buffer";
unsigned long WinPing::GetRTT()
{
return _lastRTT;
}
void WinPing::DoPing()
{
LARGE_INTEGER t;
t.HighPart = t.LowPart = 0;
SetWaitableTimer(_inputTimer, &t, _period, NULL, NULL, TRUE);
while (true)
{
int r = WaitForSingleObject(_inputTimer, _period * 2);
if (r != WAIT_OBJECT_0)
{
LogLog("InputHandler: Bad Timer return", LogError);
}
_dwRetVal = IcmpSendEcho(_hIcmpFile, _ipaddr, SendData, sizeof(SendData),
NULL, _replyBuffer, _replySize, 1000);
if (_dwRetVal != 0) {
PICMP_ECHO_REPLY pEchoReply = (PICMP_ECHO_REPLY)_replyBuffer;
struct in_addr ReplyAddr;
ReplyAddr.S_un.S_addr = pEchoReply->Address;
LogLog("\tSent icmp message to %s\n", LogDebug, _address);
if (_dwRetVal > 1) {
LogLog("\tReceived %ld icmp message responses\n", LogDebug, _dwRetVal);
LogLog("\tInformation from the first response:\n", LogDebug);
}
else {
LogLog("\tReceived %ld icmp message response\n", LogDebug, _dwRetVal);
LogLog("\tInformation from this response:\n", LogDebug);
}
LogLog("\t Received from %s\n", LogDebug, inet_ntoa(ReplyAddr));
LogLog("\t Status = %ld\n", LogDebug,
pEchoReply->Status);
LogLog("\t Roundtrip time = %ld milliseconds\n", LogDebug,
pEchoReply->RoundTripTime);
//needs synchronization here. Probably not very important
_lastRTT = pEchoReply->RoundTripTime;
LogLog("\t Roundtrip time = %ld milliseconds\n", LogDebug, _lastRTT);
IcmpCloseHandle(_hIcmpFile);
}
else {
LogLog("\tCall to IcmpSendEcho failed.\n", LogError);
LogLog("\tIcmpSendEcho returned error: %ld\n", LogError, GetLastError());
}
}
}
DWORD WINAPI WinPing::AsyncPingHandler(void* Param)
{
_inputTimer = CreateWaitableTimer(NULL, false, NULL);
if (!_inputTimer)
{
LogLog("Unable to create input waitable timer", LogError);
return 1;
}
LogLog("RTT Ping Thread started", LogDebug);
WinPing* This = (WinPing*)Param;
This->DoPing();
return 0;
}
WinPing::WinPing(char* address, int period)
:Ping(address),
_period(period)
{
// Declare and initialize variables
_ipaddr = INADDR_NONE;
_dwRetVal = 0;
_replyBuffer = NULL;
_replySize = 0;
_ipaddr = inet_addr(address);
if (_ipaddr == INADDR_NONE) {
LogLog("Not an IP Address:%s", LogError, address);
return;
}
_hIcmpFile = IcmpCreateFile();
if (_hIcmpFile == INVALID_HANDLE_VALUE) {
LogLog("\tUnable to open handle.\n", LogError);
LogLog("IcmpCreatefile returned error: %ld\n", LogError, GetLastError());
return;
}
_replySize = sizeof(ICMP_ECHO_REPLY) + sizeof(SendData);
_replyBuffer = (VOID*)malloc(_replySize);
if (_replyBuffer == NULL) {
LogLog("\tUnable to allocate memory\n", LogError);
return;
}
//Spawn thread on AsyncPingHandler()
CreateClassThread(AsyncPingHandler, this);
}
WinPing::~WinPing()
{
}
Я узнал, что намного проще, гораздо более стабильный способ выполнения ping - использовать GetRTTandHopCount. Вот пример.
UINT ip = inet_addr(serverAddress);
ULONG hopCount = 0;
ULONG RTT = 0;
if (GetRTTAndHopCount(ip, &hopCount, 30, &RTT) == TRUE) {
printf("Hops: %ld\n", hopCount);
printf("RTT: %ld\n", RTT);
}
else {
printf("Error: %ld\n", GetLastError());
}
return RTT;
Вы не вызываете GetLastError()
сразу после IcmpSendEcho()
(при вызове IcmpCreateFile()
). LogLog()
вы вызываете LogLog()
, что, вероятно, изменяет код ошибки, возвращаемый GetLastError()
, например, если он регистрируется в файле, который не может быть найден. ВСЕГДА вызывайте GetLastError()
прежде чем делать что-либо, что может вызвать системную функцию.
else {
DWORD dwErrCode = GetLastError(); // <-- call GetLastError() first
LogLog("\tCall to IcmpSendEcho failed.\n", LogError);
LogLog("\tIcmpSendEcho returned error: %u\n", LogError, dwErrCode); // <-- then use the value when needed
return;
}
while
цикл вDoPing()
бесполезно, так как все пути кода приводят кreturn
такIcmpSendEcho()
вызывается только один раз.