TIdIPWatch возвращает пустой IP-адрес (как и TIdStack) - PullRequest
0 голосов
/ 08 мая 2019

Я смотрю на простые решения в Lazarus / FPC, чтобы получить локальный IP-адрес, и, конечно, INDY кажется самым простым. Это на виртуальной машине MacOS Parallels, на которой запущена Mojave с MBP 2015. Я использую Lazarus 2.0.0RC3 и код в DYLIB. Мне нужен IP-адрес локального компьютера, чтобы получить его MAC-адрес.

Я использую TIdWatch, который должен просто возвращать локальный 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 возвращает пустую строку.

Я попытался установить Active в True (IpW.Active := True;), но это просто убивает приложение (я не уверен, почему, оно просто внезапно завершается).

Аналогично, я попытался использовать IdStack и получил ту же проблему, т.е. Возвращает пустую строку:

uses
  IdStack,
  ...

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

Опять же, этот код был из Интернета, но я просмотрел документацию для IdStack и TIdIPWatch, и, согласно моим прочтениям, должен вернуть локальный IP-адрес, указанный на веб-сайте Lazarus.

EDIT:

Согласно комментарию Реми, я исправил функцию, чтобы использовать функцию 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. Можно ли использовать TIdIPWatch или TIdStack в Lazarus / FPC для поиска локального IP-адреса виртуальной машины?

  2. Может ли кто-нибудь увидеть какие-либо явные ошибки в фрагментах кода, которые я разместил, которые препятствуют возвращению локального IP-адреса?

Я рад опубликовать больше кода и ответить на любые вопросы, если вы чувствуете, что я что-то пропустил.

EDIT2:

Таким образом, и INDY, и другие методы настроены на использование ifconfig для получения IP-адреса. Решения, похоже, ищут «net Add:»

В MacOS Mojave 10.14 IfConfig не возвращает 'net Addr:'. Он просто возвращает net, по крайней мере, в моей виртуальной машине Parallels. Вы можете изменить код соответствующим образом. Обратите внимание, что выходные данные виртуальной машины и физической системы выглядят по-разному:

Типичный вывод из моей системы 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

В чем суть, ну это сбивает с толку. На виртуальной машине вам нужно искать «inet», а на жесткой системе - «inet address». Теперь я первый, кто признает «не врач», это не моя область знаний. Поэтому я не уверен, что INDY 10 ищет «inet:» и / или «inet address:» с двоеточием в конце. Если это так, это объясняет, почему это не работает. Я буду дальше расследовать.

1 Ответ

1 голос
/ 08 мая 2019

GStack.GetLocalAddressList() реализован для OSX, поэтому я не могу сказать, почему он не возвращает никаких IP-адресов. Вам просто нужно войти в исходный код Indy с помощью отладчика и выяснить, где на самом деле происходит сбой. Внутренне, это должно вызывать финансирование OSX getifaddrs() для получения IP-адресов от ОС. Может быть, эта функция не работает в вашей виртуальной машине.

Кстати, ваш код не очень безопасен для исключений. Такие вещи, как IncUsage/DecUsage, Create/Free и т. Д. Должны быть заключены в отдельные блоки try/finally, а не в способ, которым вы это делаете (т. Е. Исключение перед IncUsage вызывает вызов DecUsage, что приводит к разбалансировке refcount). И у тебя течет память.

Ваш код должен выглядеть примерно так:

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;
...