Как использовать функцию ResolveIpNetEntry2 в Delphi?
Введение
В рамках работы с сетевыми приложениями на языке Delphi зачастую возникает потребность в получении MAC-адресов устройств, находящихся в локальной сети. Для этих целей можно использовать функцию ResolveIpNetEntry2, которая позволяет извлекать информацию из кэша ARP, без необходимости отправки новых запросов ARP.
Проблема
Разработчик столкнулся с необходимостью получения MAC-адреса устройства по его IP-адресу, используя кэш ARP. После исследования он обнаружил, что функция ResolveIpNetEntry2 должна быть подходящим инструментом для решения этой задачи. Однако, не найдя примеров кода для Delphi и соответствующих заголовков, разработчик попытался самостоятельно выполнить перевод функций и типов данных, что в итоге привело к ошибке ERROR_INVALID_PARAMETER (код 87).
Решение
В соответствии с документацией ResolveIpNetEntry2, ошибка ERROR_INVALID_PARAMETER может быть вызвана неправильным параметром, переданным в функцию. Это может быть NULL- указатель, неверно установленный адрес, неопределенные значения полей структуры MIB_IPNET_ROW2 или передача адреса петлирования. Перевод структур API был выполнен некорректно, особенно в части поля Address, которое должно быть записано внутри структуры MIB_IPNET_ROW2, а не быть внешним указателем. Также были допущены ошибки при выделении памяти и инициализации блоков памяти.
Исправленный код
Для корректной работы с ResolveIpNetEntry2 необходимо правильно определить структуры данных и обработать входные параметры. Ниже приведен пример кода, который демонстрирует, как можно использовать ResolveIpNetEntry2 для получения MAC-адреса по IP-адресу устройства в локальной сети:
{$MINENUMSIZE 4}
const
IF_MAX_PHYS_ADDRESS_LENGTH = 32;
type
NET_LUID_INFO = record
Reserved: array [0..2] of UCHAR;
NetLuidIndex: array [0..2] of UCHAR;
IfType: array [0..1] of UCHAR;
end;
NET_LUID = record
case Integer of
0: (Value: UInt64);
1: (Info: NET_LUID_INFO);
end;
NL_NEIGHBOR_STATE = (
NlnsUnreachable = 0,
NlnsIncomplete,
NlnsProbe,
NlnsDelay,
NlnsStale,
NlnsReachable,
NlnsPermanent,
NlnsMaximum);
PSOCKADDR_INET = ^SOCKADDR_INET;
SOCKADDR_INET = record
case Integer of
0: (Ipv4: SOCKADDR_IN);
1: (Ipv6: SOCKADDR_IN6);
2: (si_family: ADDRESS_FAMILY);
end;
NETIO_STATUS = DWORD;
NET_IFINDEX = ULONG;
PMIB_IPNET_ROW2 = ^MIB_IPNET_ROW2;
MIB_IPNET_ROW2 = record
Address: SOCKADDR_INET;
InterfaceIndex: NET_IFINDEX;
InterfaceLuid: NET_LUID;
PhysicalAddress: array [0..IF_MAX_PHYS_ADDRESS_LENGTH - 1] of UCHAR;
PhysicalAddressLength: ULONG;
State: NL_NEIGHBOR_STATE;
Flags: UCHAR;
ReachabilityTime: record
case Integer of
0: (LastReachable: ULONG);
1: (LastUnreachable: ULONG);
end;
function IsRouter: Boolean;
function IsUnreachable;
end;
function MIB_IPNET_ROW2.IsRouter: Boolean;
begin
Result := (Flags and $01) <> 0;
end;
function MIB_IPNET_ROW2.IsUnreachable;
begin
Result := (Flags and $02) <> 0;
end;
function ResolveIp(const AIp: String; AIfIndex: ULONG): String;
type
TResolveIpNetEntry2Func = function (Row: PMIB_IPNET_ROW2; const SourceAddress: PSOCKADDR_INET): NETIO_STATUS; stdcall;
const
IphlpApiDll = 'iphlpapi.dll';
var
hIphlpApiDll: THandle;
ResolveIpNetEntry2: TResolveIpNetEntry2Func;
status: NETIO_STATUS;
Row: PMIB_IPNET_ROW2;
begin
Result := '';
hIphlpApiDll := LoadLibrary(IphlpApiDll);
if hIphlpApiDll = 0 then
Exit;
try
ResolveIpNetEntry2 := GetProcAddress(hIphlpApiDll, 'ResolveIpNetEntry2');
if not Assigned(ResolveIpNetEntry2) then
Exit;
SetLength(Row, SizeOf(MIB_IPNET_ROW2));
ZeroMemory(@Row[0], SizeOf(MIB_IPNET_ROW2));
if InetPton(AF_INET, PChar(AIp), @(Row.Address.Ipv4.sin_addr)) = 1 then
Row.Address.Ipv4.sin_family := AF_INET
else
if InetPton(AF_INET6, PChar(AIp), @(Row.Address.Ipv6.sin6_addr)) = 1 then
Row.Address.Ipv6.sin6_family := AF_INET6
else
Exit;
Row.InterfaceIndex := AIfIndex;
status := ResolveIpNetEntry2(@Row, nil);
// ...
finally
FreeLibrary(hIphlpApiDll);
end;
end;
Альтернативный метод
Также возможно использовать функцию ResolveIp без динамического выделения памяти для Row:
function ResolveIp(const AIp: String; AIfIndex: ULONG): String;
var
Row: MIB_IPNET_ROW2;
begin
// ...
ZeroMemory(@Row, SizeOf(Row));
// ...
status := ResolveIpNetEntry2(@Row, nil);
// ...
end;
В этом случае Row является локальной переменной на стеке, и его содержимое инициализируется нулями. Важно различать обращение к локальной переменной и к динамически выделенной памяти.
Заключение
Использование функции ResolveIpNetEntry2 позволяет извлекать MAC-адреса устройств из кэша ARP, что может быть полезно для различных сетевых приложений на Delphi. Важно правильно определить структуры данных и обработать входные параметры для корректной работы функции.
Описание Context: Запрос на использование функции `ResolveIpNetEntry2` в Delphi для получения MAC-адреса устройства по его IP-адресу.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.