-
Всем доброго времени суток! Задача избитая, но полноценного решения не нашел. Нужно сделать ping на Delphi. Нашел вроде хороший пример http://www.delphimaster.ru/articles/icmp.html , но не хватает мозгов как сделать, что бы размер буффера можно бло указывать произвольно? Не хватает мозгов переделать на динамический массив буффера данных. Кроме того хотелось бы услышать мнение по правильности этого кода, есть мнение, что этот код может вызывать утечки памяти... И еще интересно - в Delphi XE случайно не сделали "обертку" под использование функций из ICMP.DLL?
-
-
Да, мне нужно н Делфи. В общем вместо pingBuffer : array [0..31] of AnsiChar; я написал pingBuffer : array of AnsiChar; Потом инициализирую переменную SetLength(pingBuffer, 1452); и заменил везде sizeof(pingBuffer) на Length(pingBuffer) Адрес массива передаю также: pIpe.Data := @pingBuffer; Вроде все работает, но вопрос - правильно ли я все сделал? Больше всего волнует вопрос: передача адреса на статический и динамический массив одинаково выполняется в Делфи? Я имею ввиду синтаксически...
-
Неодинаково. @StaticArr = @StaticArr[0] — указатель на первый элемент @DynArr[0] — указатель на первый элемент @DynArr — указатель на указатель на первый элемент
-
Спасибо! Подправил
-
-
-
unit Pings;
interface
USES windows, WinSock, Error;
type
ip_option_information = packed record Ttl : byte; Tos : byte; Flags : byte; OptionsSize : byte; OptionsData : Pointer; end;
icmp_echo_reply = packed record
Address : u_long; Status : u_long; RTTime : u_long; DataSize : u_short; Reserved : u_short; Data : Pointer; Options : ip_option_information; end;
PIPINFO = ^ip_option_information;
PVOID = Pointer;
var
ping_error: DWORD;
Ping_error_message: string;
function IcmpCreateFile() : THandle; stdcall; external 'ICMP.DLL' name 'IcmpCreateFile';
function IcmpCloseHandle(IcmpHandle : THandle) : BOOL; stdcall; external 'ICMP.DLL' name 'IcmpCloseHandle';
function IcmpSendEcho(
IcmpHandle : THandle; DestAddress : u_long; RequestData : PVOID; RequestSize : Word; RequestOptns : PIPINFO; ReplyBuffer : PVOID; ReplySize : DWORD; Timeout : DWORD ) : DWORD; stdcall; external 'ICMP.DLL' name 'IcmpSendEcho';
function GetPing(HostName: String): Integer;
implementation
function GetPing(HostName: String): Integer;
var
hIP : THandle;
pingBuffer : array [0..31] of Char;
pIpe : ^icmp_echo_reply;
pHostEn : PHostEnt;
wVersionRequested : WORD;
lwsaData : WSAData;
error : DWORD;
destAddress : In_Addr;
ping: Integer;
begin
hIP := IcmpCreateFile();
Result := -1;
GetMem(pIpe, sizeof(icmp_echo_reply) + sizeof(pingBuffer));
pIpe.Data := @pingBuffer;
pIpe.DataSize := sizeof(pingBuffer);
wVersionRequested := MakeWord(1,1);
error := WSAStartup(wVersionRequested, lwsaData);
if (error <> 0) then
begin
ping_error := error;
Ping_error_message := 'Error: WSAStartup() to exit; ' + SystemErrorMessage(error);
Exit;
end;
pHostEn := gethostbyname(PAnsiChar(AnsiString(HostName)));
error := GetLastError();
if (error <> 0) then
begin
ping_error := error;
Ping_error_message := 'Error: gethostbyname() to exit; ' + SystemErrorMessage(error);
Exit;
end;
destAddress := PInAddr(pHostEn^.h_addr_list^)^;
IcmpSendEcho(hIP,
destAddress.S_addr,
@pingBuffer,
sizeof(pingBuffer),
Nil,
pIpe,
sizeof(icmp_echo_reply) + sizeof(pingBuffer),
500);
error := GetLastError();
if (error <> 0) then
begin
ping_error := error;
Ping_error_message := 'Error: IcmpSendEcho() to exit; ' + SystemErrorMessage(error);
Exit;
end;
Result := pIpe.RTTime;
if Result = 0 then Result := 1;
IcmpCloseHandle(hIP);
WSACleanup();
FreeMem(pIpe);
end;
end.
-
Пинг реализован в отдельном потоке (модуль может быть с ошибками) unit ThrPings;
interface
uses
System.Classes, System.SysUtils , Dialogs, Windows, WinSock, Error;
type
ip_option_information = packed record Ttl : byte; Tos : byte; Flags : byte; OptionsSize : byte; OptionsData : Pointer; end;
icmp_echo_reply = packed record
Address : u_long; Status : u_long; RTTime : u_long; DataSize : u_short; Reserved : u_short; Data : Pointer; Options : ip_option_information; end;
PIPINFO = ^ip_option_information;
PVOID = Pointer;
type
ThreadPings = class(TThread)
private
FHostName : AnsiString;
FPingResult : Integer;
FInterval : Word;
procedure SetHosName(StrValue: String);
procedure SetPingResult(IntValue: Integer);
procedure SetInterval(IntValue: WORD);
function GetInterval: WORD;
function GetHostName: String;
function GetPingResult: Integer;
protected
hIP : THandle;
pIpe : ^icmp_echo_reply;
wVersionRequested : WORD;
lwsaData : WSAData;
pHostEn : PHostEnt;
destAddress : In_Addr;
error : DWORD;
pingBuffer : array [0..31] of Char;
StrSendText: String;
procedure SendMemo;
procedure Execute; override;
public
LastErrorMessage: string;
Property PingResult: Integer read GetPingResult write SetPingResult;
Property HostName: string read GetHostName write SetHosName;
Property Interval: WORD read GetInterval write SetInterval;
procedure Ping;
constructor Create;
destructor Destroy; Override;
end;
function IcmpCreateFile() : THandle; stdcall; external 'ICMP.DLL' name 'IcmpCreateFile';
function IcmpCloseHandle(IcmpHandle : THandle) : BOOL; stdcall; external 'ICMP.DLL' name 'IcmpCloseHandle';
function IcmpSendEcho(
IcmpHandle : THandle; DestAddress : u_long; RequestData : PVOID; RequestSize : Word; RequestOptns : PIPINFO; ReplyBuffer : PVOID; ReplySize : DWORD; Timeout : DWORD ) : DWORD; stdcall; external 'ICMP.DLL' name 'IcmpSendEcho';
implementation
USES UFrmMain;
constructor ThreadPings.Create;
begin
inherited Create(True);
hIP := IcmpCreateFile();
GetMem(pIpe, sizeof(icmp_echo_reply) + sizeof(pingBuffer));
pIpe.Data := @pingBuffer;
pIpe.DataSize := sizeof(pingBuffer);
wVersionRequested := MakeWord(1,1);
error := WSAStartup(wVersionRequested, lwsaData);
if (error <> 0) then
begin
LastErrorMessage := 'Error: gethostbyname() ' + SystemErrorMessage(error);
Terminate;
end;
end;
destructor ThreadPings.Destroy;
begin
IcmpCloseHandle(hIP);
WSACleanup();
FreeMem(pIpe);
inherited;
end;
procedure ThreadPings.Execute;
var BgnTime: Cardinal;
begin
NameThreadForDebugging('TPings');
BgnTime := GetTickCount;
While Not Terminated do
begin
if LastErrorMessage <> '' then Continue;
if ((GetTickCount - BgnTime) div 1000) >= FInterval Then
begin
IcmpSendEcho(hIP,
destAddress.S_addr,
@pingBuffer,
sizeof(pingBuffer),
Nil,
pIpe,
sizeof(icmp_echo_reply) + sizeof(pingBuffer),
5000);
error := GetLastError();
if (error <> 0) then
begin
LastErrorMessage := 'Error: IcmpSendEcho() ' + SystemErrorMessage(error);
Continue;
end;
if pIpe.RTTime = 0 then PingResult := 1
else PingResult := pIpe.RTTime;
StrSendText := 'Ping: '+IntToStr(PingResult);
Synchronize(SendMemo);
BgnTime := GetTickCount;
end;
sleep(50);
end;
end;
function ThreadPings.GetHostName: String;
begin
Result := FHostName;
end;
function ThreadPings.GetInterval: WORD;
begin
Result := FInterval;
end;
function ThreadPings.GetPingResult: Integer;
begin
Result := FPingResult;
end;
procedure ThreadPings.Ping;
begin
Resume;
end;
procedure ThreadPings.SendMemo;
begin
FrmMain.mm.lines.add(StrSendText);
end;
procedure ThreadPings.SetHosName(StrValue: String);
begin
FPingResult := 0;
pHostEn := gethostbyname(PAnsiChar(AnsiString(StrValue)));
error := GetLastError();
if (error <> 0) then
begin
LastErrorMessage := 'Error: gethostbyname() ' + SystemErrorMessage(error);
ShowMessage(LastErrorMessage);
Exit;
end;
destAddress := PInAddr(pHostEn^.h_addr_list^)^;
LastErrorMessage := '';
end;
procedure ThreadPings.SetInterval(IntValue: WORD);
begin
FInterval := IntValue;
if IntValue < 3 then FInterval := 3;
if IntValue > 300 then FInterval := 300;
end;
procedure ThreadPings.SetPingResult(IntValue: Integer);
begin
FPingResult := IntValue;
end;
end.
-
Пояснения модуля ThrPings работающего в потоке Классический пример из поста cryptologic © (16.08.18 02:10) [7] т.е. функцию function GetPing(HostName: String): Integer; ее части кода раскидал по процедурам класса потока: 1. выделение буфера GetMem(pIpe, sizeof(icmp_echo_reply) + sizeof(pingBuffer)); вписал в метод ThreadPings.Create; а уничтожение буфера в destroy т.е. буфер создается один раз и используется на всем протяжении жизни потока. И вот возникает проблема правомерно ли так делать? периодически вываливается ошибка о недостаточности ресурсов. Может нужно буфер создавать и освобождать при каждом вызове пинга? Кто встречался с подобным отпешитесь?
-
Модуль Error.pas для для обоих вышеупомянутых модулей Позволяет получить локализованный ответ об ошибке согласно установленной языковой версии windows Unit Error;
interface
Uses Windows;
function SystemErrorMessage(ErrorCode: Integer): string;
function GetSysErrorMessage(ErrorCode: Integer): String;
procedure SaveErrorMessage(DebugMsg: String);
var
LAST_ERROR_MESSAGE : String;
implementation
procedure SaveErrorMessage(DebugMsg: String);
begin
if DebugMsg <> '' then
LAST_ERROR_MESSAGE := DebugMsg+' '+GetSysErrorMessage(GetLastError)
else LAST_ERROR_MESSAGE := GetSysErrorMessage(GetLastError)
end;
function GetSysErrorMessage(ErrorCode: Integer): String;
Var s: string;
begin
Str(ErrorCode:0,S);
Result:='System Error. Code: '+s+' '+SystemErrorMessage(ErrorCode)+'.';
end;
function SystemErrorMessage(ErrorCode: Integer): string;
var
Buffer : array[0..255] of Char;
Len : Integer;
begin
Len := FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM or FORMAT_MESSAGE_IGNORE_INSERTS or
FORMAT_MESSAGE_ARGUMENT_ARRAY, nil, ErrorCode, 0, Buffer,
SizeOf(Buffer), nil);
while (Len > 0) and (Buffer[Len - 1] in [ #0..#32, '.']) do Dec(Len);
SetString(Result, Buffer, Len);
end;
end.
-
Почему пинг в отдельном потоке, потому что при отсутствии узла и цикличном пинговании у приложения возникают невероятные фризы Мини инструкция как использовать модуль: var Tpings: ThreadPings;
Tpings := ThreadPings.Create; TPings.Interval := 3; TPings.HostName := 'Yandex.ru'; TPings.Ping; TPings.PingResult mm.Lines.Add('TPings.PingResult: ' + IntToStr(TPings.PingResult));
-
Удалено модератором
-
Офигеть! Через столько лет вспомнили нашу с Серегой статью про icmp. Только почему из авторов убрали Сергея?
|