TIdIPWatch returns 空白 IP(与 TIdStack 一样)

TIdIPWatch returns blank IP (as does TIdStack)

我正在寻找 Lazarus/FPC 中获取本地 IP 地址的简单解决方案,当然 INDY 似乎最简单。这是在 2015 MBP 上的并行 MacOS VM 运行 Mojave 运行 上。我正在使用 Lazarus 2.0.0RC3,代码在 DYLIB 中。我需要本地机器的 IP 地址来获取它的 MAC 地址。

我正在使用 TIdWatch,它应该只是 return 本地 IP 地址(来自其 CurrentIP 属性)。我喜欢这两种解决方案,IdStack 下面会详细介绍,因为代码非常简单,但是当然,如​​果代码不起作用,再简单也没有任何好处:

uses
  IdBaseComponent,
  IdComponent,
  IdIPWatch,
  ...

function getLocalIP: string;
var
   IPW: TIdIPWatch;
begin
  IpW := TIdIPWatch.Create(nil);
  try
    if IpW.LocalIP <> '' then
      Result := IpW.LocalIP;
    ShowMessage('IP: ' + Result);
  finally
    IpW.Free;
  end;
end;

实际IP地址是192.168.1.25,但是调用IdPWatch.LocalIP return是一个空字符串。

我尝试将 Active 设置为 True (IpW.Active := True;),但这只会终止应用程序(我不确定为什么,它只是突然终止)。

同样,我尝试使用 IdStack 并遇到同样的问题,即。它 Returns 一个空字符串:

uses
  IdStack,
  ...

function GetLocalIP : String;
begin
  TIdStack.IncUsage;
  try
    Result := GStack.LocalAddress;
    ShowMessage('IP: ' + Result);
  finally
    TIdStack.DecUsage;
  end;
end;

同样,此代码来自互联网,但我已经查找了 IdStackTIdIPWatch 的文档,根据我的阅读,应该 return 本地 IP 地址如 Lazarus 网站所述。

编辑:

根据 Remy 的评论,我修改了函数以使用 GStack.GetLocalAddressList 函数,而不是我之前使用的已弃用函数。请注意,结果是相同的,并且 IPList(TIdStackLocalAddressList 变量)没有元素:

function getLocalIP: string;
var
  IPList: TIdStackLocalAddressList;
  IPStrings: TStringList;
  i: integer;

begin
  try
    try
      Result:='';
      IPList:=TIdStackLocalAddressList.Create;
      IPStrings := TStringList.create;

      TIdStack.IncUsage;
      GStack.GetLocalAddressList(IPList);
      if IPList.count > 0 then
      begin
        WriteLog('DEBUG', 'No of Addresses: ' + inttostr(IPList.count));
        for i := 0 to IPList.Count - 1 do
        begin
          if IPList[i].IPVersion = Id_IPv4 then
          begin
            IPStrings.Add(IPList[i].IPAddress+':'+ TIdStackLocalAddressIPv4(IPList[i]).SubNetMask);
          end;
        end;
        // show IP Addresses in the log file
        if IPStrings.Count > 0 then
        begin
          for i := 0 to IPStrings.Count -1 do
            WriteLog('DEBUG', 'IP Address #' + inttostr(i) + ': ' + IPStrings[i]);
          Result := IPStrings[0];
          WriteLog('DEBUG', 'IP: ' + Result);
        end
        else
          WriteLog('DEBUG', 'IPStrings has no entries');
      end
      else
        WriteLog('DEBUG', 'TIdStackLocalAddressList has no entries');
    except
      On E:Exception do
      begin
        Result := '';
        WriteLog('ERROR', 'IP Error: ' + E.message);
      end;
    end;
  finally
    TIdStack.DecUsage;
  end;

end;

所以我的问题是:

  1. 我可以在Lazarus/FPC中使用TIdIPWatchTIdStack来查找虚拟机的本地IP地址吗?

  2. 任何人都可以看到我 post 阻止 return 本地 IP 地址的代码片段中的任何明显错误吗?

我很乐意 post 更多代码,如果您觉得我遗漏了任何问题,我会回答任何问题。

编辑2:

所以 INDY 和其他方法都设置为使用 ifconfig 来获取 IP 地址。解决方案似乎在寻找 'net Add:'

在 MacOS Mojave 10.14 中,IfConfig 不会 return 'net Addr:'。它只是 returns 'net',至少在我的 Parallels VM 中是这样。您可能希望相应地调整代码。请注意 VM 和物理系统的输出似乎不同:

我的 Mojave Parallels VM 系统的典型输出(<> 填充块引用):

kevin$ ifconfig
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
    options=1203<RXCSUM,TXCSUM,TXSTATUS,SW_TIMESTAMP>
    inet 127.0.0.1 netmask 0xff000000 
    inet6 ::1 prefixlen 128 
    inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1 
    nd6 options=201<PERFORMNUD,DAD>
gif0: flags=8010<POINTOPOINT,MULTICAST> mtu 1280
stf0: flags=0<> mtu 1280
UHC29: flags=0<> mtu 0
EHC253: flags=0<> mtu 0
XHC221: flags=0<> mtu 0
en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
    options=2b<RXCSUM,TXCSUM,VLAN_HWTAGGING,TSO4>
    ether 00:1c:42:72:74:a3 
    inet6 fe80::3a:650d:9b24:c9c5%en0 prefixlen 64 secured scopeid 0x7 
    inet 192.168.1.25 netmask 0xffffff00 broadcast 192.168.1.255
    nd6 options=201<PERFORMNUD,DAD>
    media: autoselect (1000baseT <full-duplex>)

硬件系统的典型输出运行 Mojave(实际IP地址为en0(5),网络地址):

kevin$ ifconfig

awdl0 (8):
  flags         UP BROADCAST RUNNING PROMISC SIMPLEX MULTICAST
  mtu           1484

bridge0 (10):
  flags         UP BROADCAST NOTRAILERS RUNNING SIMPLEX MULTICAST
  mtu           1500

en0 (5):
  inet address  192.168.1.167
  netmask       255.255.255.0
  broadcast     192.168.1.255
  flags         UP BROADCAST NOTRAILERS RUNNING PROMISC SIMPLEX MULTICAST
  mtu           1500

en1 (7):
  flags         UP BROADCAST NOTRAILERS RUNNING PROMISC SIMPLEX MULTICAST
  mtu           1500

en2 (9):
  flags         UP BROADCAST NOTRAILERS RUNNING PROMISC SIMPLEX MULTICAST
  mtu           1500

lo0 (1):
  inet address  127.0.0.1
  netmask       255.0.0.0
  flags         UP LOOPBACK RUNNING MULTICAST
  mtu           16384

p2p0 (6):
  flags         UP BROADCAST RUNNING SIMPLEX MULTICAST
  mtu           2304

utun0 (11):
  flags         UP POINTOPOINT RUNNING MULTICAST
  mtu           2000

utun1 (12):
  flags         UP POINTOPOINT RUNNING MULTICAST
  mtu           1380

vnic0 (13):
  inet address  10.211.55.2
  netmask       255.255.255.0
  broadcast     10.211.55.255
  flags         UP BROADCAST RUNNING SIMPLEX MULTICAST
  mtu           1500

vnic1 (14):
  inet address  10.37.129.2
  netmask       255.255.255.0
  broadcast     10.37.129.255
  flags         UP BROADCAST RUNNING SIMPLEX MULTICAST
  mtu           1500

底线是什么,这很令人困惑。在 VM 上您需要搜索 'inet ',在硬系统上您需要搜索 'inet address '。现在我首先承认'not a doctor',这不是我的专业领域。所以我不确定 INDY 10 是否正在搜索 'inet:' and/or 'inet address:' 末尾有冒号。如果是这样就可以解释为什么它不起作用。我会进一步调查。

GStack.GetLocalAddressList() 是为 OSX 实现的,所以我不能说为什么它不返回任何 IP 地址。您只需要使用调试器进入 Indy 的源代码并找出实际发生故障的位置。在内部,它应该调用 OSX getifaddrs() 资金从 OS 获取 IP 地址。也许该功能在您的 VM 中不起作用。

附带说明一下,您的代码不是很 exception-safe。 IncUsage/DecUsageCreate/Free 等内容应该包装在单独的 try/finally 块中,而不是按照您的方式进行包装(即 IncUsage 之前的异常会导致 DecUsage 被调用,使引用计数不平衡)。你正在泄漏内存。

您的代码应该更像这样:

function getLocalIP: string;
var
  IPList: TIdStackLocalAddressList;
  IPStrings: TStringList;
  i: integer;
begin
  Result := '';
  try
    IPList := TIdStackLocalAddressList.Create;
    try
      TIdStack.IncUsage;
      try
        GStack.GetLocalAddressList(IPList);
      finally
        TIdStack.DecUsage;
      end;

      if IPList.Count > 0 then
      begin
        WriteLog('DEBUG', 'No of Addresses: ' + IntToStr(IPList.Count));

        IPStrings := TStringList.Create;
        try
          for i := 0 to IPList.Count - 1 do
          begin
            if IPList[i].IPVersion = Id_IPv4 then
            begin
              IPStrings.Add(IPList[i].IPAddress + ':' + TIdStackLocalAddressIPv4(IPList[i]).SubNetMask);
            end;
          end;

          // show IP Addresses in the log file
          if IPStrings.Count > 0 then
          begin
            for i := 0 to IPStrings.Count -1 do
              WriteLog('DEBUG', 'IP Address #' + IntToStr(i) + ': ' + IPStrings[i]);
            Result := IPStrings[0];
            WriteLog('DEBUG', 'IP: ' + Result);
          end else
            WriteLog('DEBUG', 'IPStrings has no entries');
        finally
          IPStrings.Free;
        end;
      end else
        WriteLog('DEBUG', 'TIdStackLocalAddressList has no entries');
    finally
      IPList.Free;
    end;
  except
    On E: Exception do
    begin
      Result := '';
      WriteLog('ERROR', 'IP Error: ' + E.message);
    end;
  end;
end;