Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Delphi中使用ResolveIpNetEntry2?_Delphi_Winapi - Fatal编程技术网

如何在Delphi中使用ResolveIpNetEntry2?

如何在Delphi中使用ResolveIpNetEntry2?,delphi,winapi,Delphi,Winapi,我想从同一本地网络中主机的IP获取MAC地址。我更喜欢从本地缓存中获取这些信息,而不是发送一个新的ARP请求。我发现这应该是我需要的 不幸的是,我没有找到任何带有该函数的Delphi代码示例。更糟糕的是,我甚至没有找到该函数及其数据类型的任何Delphi头。所以我试着自己转换它们。嗯,它可以编译,但我得到了ERROR\u INVALID\u参数(87),所以显然我转换了一些错误的东西 有人能告诉我怎么改正吗 const IF_MAX_PHYS_ADDRESS_LENGTH = 32; typ

我想从同一本地网络中主机的IP获取MAC地址。我更喜欢从本地缓存中获取这些信息,而不是发送一个新的ARP请求。我发现这应该是我需要的

不幸的是,我没有找到任何带有该函数的Delphi代码示例。更糟糕的是,我甚至没有找到该函数及其数据类型的任何Delphi头。所以我试着自己转换它们。嗯,它可以编译,但我得到了ERROR\u INVALID\u参数(87),所以显然我转换了一些错误的东西

有人能告诉我怎么改正吗

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信息