如何在Delphi中使用ResolveIpNetEntry2?
我想从同一本地网络中主机的IP获取MAC地址。我更喜欢从本地缓存中获取这些信息,而不是发送一个新的ARP请求。我发现这应该是我需要的 不幸的是,我没有找到任何带有该函数的Delphi代码示例。更糟糕的是,我甚至没有找到该函数及其数据类型的任何Delphi头。所以我试着自己转换它们。嗯,它可以编译,但我得到了ERROR\u INVALID\u参数(87),所以显然我转换了一些错误的东西 有人能告诉我怎么改正吗如何在Delphi中使用ResolveIpNetEntry2?,delphi,winapi,Delphi,Winapi,我想从同一本地网络中主机的IP获取MAC地址。我更喜欢从本地缓存中获取这些信息,而不是发送一个新的ARP请求。我发现这应该是我需要的 不幸的是,我没有找到任何带有该函数的Delphi代码示例。更糟糕的是,我甚至没有找到该函数及其数据类型的任何Delphi头。所以我试着自己转换它们。嗯,它可以编译,但我得到了ERROR\u INVALID\u参数(87),所以显然我转换了一些错误的东西 有人能告诉我怎么改正吗 const IF_MAX_PHYS_ADDRESS_LENGTH = 32; typ
const
IF_MAX_PHYS_ADDRESS_LENGTH = 32;
type
NET_LUID = record
case Word of
1: (Value: Int64;);
2: (Reserved: Int64;);
3: (NetLuidIndex: Int64;);
4: (IfType: Int64;);
end;
NL_NEIGHBOR_STATE = (
NlnsUnreachable=0,
NlnsIncomplete,
NlnsProbe,
NlnsDelay,
NlnsStale,
NlnsReachable,
NlnsPermanent,
NlnsMaximum);
PMIB_IPNET_ROW2 = ^MIB_IPNET_ROW2;
MIB_IPNET_ROW2 = record
Address: LPSOCKADDR; //SOCKADDR_INET
InterfaceIndex: ULONG; //NET_IFINDEX
InterfaceLuid: NET_LUID;
PhysicalAddress: array [0..IF_MAX_PHYS_ADDRESS_LENGTH - 1] of UCHAR;
PhysicalAddressLength: ULONG;
State: NL_NEIGHBOR_STATE;
Union: record
case Integer of
0: (IsRouter: Boolean;
IsUnreachable: Boolean);
1: (Flags: UCHAR);
end;
ReachabilityTime: record
case Integer of
0: (LastReachable: ULONG);
1: (LastUnreachable: ULONG);
end;
end;
function ResolveIp(const AIp: String; AIfIndex: ULONG): String;
type
TResolveIpNetEntry2Func = function (Row: PMIB_IPNET_ROW2; const SourceAddress: LPSOCKADDR): DWORD; stdcall; //NETIOAPI_API
const
IphlpApiDll = 'iphlpapi.dll';
var
hIphlpApiDll: THandle;
ResolveIpNetEntry2: TResolveIpNetEntry2Func;
dw: DWORD;
Row: PMIB_IPNET_ROW2;
SourceAddress: LPSOCKADDR;
IpAddress: LPSOCKADDR;
begin
hIphlpApiDll := LoadLibrary(IphlpApiDll);
if hIphlpApiDll = 0 then
Exit;
ResolveIpNetEntry2 := GetProcAddress(hIphlpApiDll, 'ResolveIpNetEntry2');
if (@ResolveIpNetEntry2 = nil) then
Exit;
IpAddress := AllocMem(SizeOf(IpAddress));
IpAddress.sa_family := AF_INET;
IpAddress.sa_data := PAnsiChar(AIp);
Row := AllocMem(SizeOf(Row));
Row.Address := IpAddress;
Row.InterfaceIndex := AIfIndex;
SourceAddress := 0;
dw := ResolveIpNetEntry2(Row, SourceAddress);
//...
end;
根据:
如果函数失败,返回值为以下错误代码之一
错误\u无效\u参数传递给函数的参数无效。如果在Row参数中传递空指针,Row参数指向的MIB_IPNET_ROW2的地址成员未设置为有效的IPv4或IPv6地址,或者Row参数指向的MIB_IPNET_ROW2的InterfaceId或InterfaceIndex成员均未指定,则返回此错误。如果在地址成员中传递了环回地址,也会返回此错误 您对API结构的翻译通常是不正确的。有几个字段出错,这会影响字段偏移和大小。例如位字段。但更重要的是,您对
MIB\u IPNET\u ROW2.Address
字段的声明是完全错误的。它根本不是指向外部SOCKADDR
记录的指针。它是一个SOCKADDR\u INET
记录,存在于MIB\u IPNET\u行2
内部,而不是外部
您也没有正确分配或初始化内存块,这也会影响上述文档
而且,即使您正确地翻译了API,您也没有正确地将AIp
字符串转换为SOCKADDR
。您不能简单地键入cast,您需要使用inet\u addr()
、InetPton()
或其他等效函数将其从字符串转换为网络地址整数。因此,这也适用于上述文档
幸运的是,您的代码没有完全崩溃
请尝试类似以下内容:
{$MINENUMSIZE 4}
const
IF_MAX_PHYS_ADDRESS_LENGTH = 32;
type
NET_LUID_INFO = record
Reserved: array [0..2] of UCHAR; // ULONG64:24
NetLuidIndex: array [0..2] of UCHAR; // ULONG64:24
IfType: array [0..1] of UCHAR; // ULONG64:16
//
// TODO: if you need to access these values, define
// some property getters/setters to translate them
// to/from UInt64...
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;
New(Row);
try
ZeroMemory(Row, 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
Dispose(Row);
end;
finally
FreeLibrary(hIphlpApiDll);
end;
end;
根据:
如果函数失败,返回值为以下错误代码之一
错误\u无效\u参数传递给函数的参数无效。如果在Row参数中传递空指针,Row参数指向的MIB_IPNET_ROW2的地址成员未设置为有效的IPv4或IPv6地址,或者Row参数指向的MIB_IPNET_ROW2的InterfaceId或InterfaceIndex成员均未指定,则返回此错误。如果在地址成员中传递了环回地址,也会返回此错误 您对API结构的翻译通常是不正确的。有几个字段出错,这会影响字段偏移和大小。例如位字段。但更重要的是,您对
MIB\u IPNET\u ROW2.Address
字段的声明是完全错误的。它根本不是指向外部SOCKADDR
记录的指针。它是一个SOCKADDR\u INET
记录,存在于MIB\u IPNET\u行2
内部,而不是外部
您也没有正确分配或初始化内存块,这也会影响上述文档
而且,即使您正确地翻译了API,您也没有正确地将AIp
字符串转换为SOCKADDR
。您不能简单地键入cast,您需要使用inet\u addr()
、InetPton()
或其他等效函数将其从字符串转换为网络地址整数。因此,这也适用于上述文档
幸运的是,您的代码没有完全崩溃
请尝试类似以下内容:
{$MINENUMSIZE 4}
const
IF_MAX_PHYS_ADDRESS_LENGTH = 32;
type
NET_LUID_INFO = record
Reserved: array [0..2] of UCHAR; // ULONG64:24
NetLuidIndex: array [0..2] of UCHAR; // ULONG64:24
IfType: array [0..1] of UCHAR; // ULONG64:16
//
// TODO: if you need to access these values, define
// some property getters/setters to translate them
// to/from UInt64...
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;
New(Row);
try
ZeroMemory(Row, 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
Dispose(Row);
end;
finally
FreeLibrary(hIphlpApiDll);
end;
end;
为什么不直接获取ARP-A IPNUMBER输出并从中提取MAC地址呢?这里您可以看到一个如何捕获ARP命令输出的示例。我知道如何通过命令行发送ARP请求,或者更好地通过API(SendArp())发送ARP请求。由于很多原因,在这种情况下,我更喜欢获取缓存的信息,而不是发送新的请求。你说得对:arp.exe执行缓存查找,而不是发送新的查找。但最终它只是在内部执行一些API调用,很可能是ResolveIpNetEntry2。我总是喜欢直接使用API,而不是解析命令行输出。为什么不直接获取ARP-a IPNUMBER输出并从中提取MAC地址呢?这里可以看到一个如何捕获ARP命令输出的示例。我知道如何通过命令行发送ARP请求,或者更好地通过API(SendArp())发送ARP请求。由于很多原因,在这种情况下,我更喜欢获取缓存的信息,而不是发送新的请求。你说得对:arp.exe执行缓存查找,而不是发送新的查找。但最终它只是在内部执行一些API调用,很可能是ResolveIpNetEntry2。我总是喜欢直接使用API,而不是解析命令行输出。非常感谢!是否包括地址_家族(ULONG?)和InetPton?德尔福绝地没有为Vista和更高版本提供任何更新的头文件,这真是一件微不足道的事。
ADDRESS\u FAMILY
实际上是一个USHORT
,它是在德尔福的ShellAPI
单元中定义的(不过放在别处)。我认为任何Delphi单元都不会声明inetpon()
,但您可以自己在代码中声明,它在Ws2\u 32.dll
中。我可以让Ansi版本的inetpon工作,但不幸的是,我不知道如何处理WSAAPI信息