I'm looking at simple solutions in Lazarus/FPC to get the local IP Address and certainly INDY seems simplest. This is on a parallels MacOS VM running Mojave running on a 2015 MBP. I'm using Lazarus 2.0.0RC3 and the code is in a DYLIB. I need the IP address of the local machine to get it's MAC Address.
I'm using TIdWatch
, which should simply return the local IP address (from its CurrentIP
property). I like both solutions, IdStack
detailed further below, because the code is very simple, but of course, no matter how simple is not of any benefit if the code does not work:
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;
The actual IP address is 192.168.1.25, but the call to IdPWatch.LocalIP
returns a blank string.
I tried setting Active
to True (IpW.Active := True;
), but that just kills the application (I am unsure why, it simply terminates abruptly).
Likewise, I have tried using IdStack
and get the same issue, ie. it Returns a blank string:
uses
IdStack,
...
function GetLocalIP : String;
begin
TIdStack.IncUsage;
try
Result := GStack.LocalAddress;
ShowMessage('IP: ' + Result);
finally
TIdStack.DecUsage;
end;
end;
Again, this code was from the internet, but I have looked up documentation for both IdStack
and TIdIPWatch
, and according to my readings, should return the local IP address as stated on the Lazarus website.
EDIT:
As per Remy's comment, I have amended the function to use the GStack.GetLocalAddressList function rather than the deprecated functions I was previously using. Note that the result is the same and that IPList (the TIdStackLocalAddressList variable) has no elements:
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;
So my questions:
Can I use TIdIPWatch
or TIdStack
in Lazarus/FPC to find the local IP address of the VM?
Can anyone see any glaring errors in the code snippets I have posted preventing the return of the local IP address?
I am happy to post more code, and answer any questions if you feel I have left anything out.
EDIT2:
So both INDY and other methods set to use ifconfig to get the IP address. The solutions seem to be looking for 'net Add:'
In MacOS Mojave 10.14, IfConfig does not return 'net Addr:'. It simply returns 'net', at least in my Parallels VM. You may wish to adapt the code accordingly. Note the output from a VM and a physical system appear to be different:
Typical output from my Mojave Parallels VM system (the <> stuff up the block quote):
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>)
Typical output from hard systems running Mojave (the actual IP address is at en0 (5), net address):
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
What's the bottom line, well it's confusing. On a VM you need to search for 'inet ' and on a hard system you need to search for 'inet address '. Now I am the first to admit 'not a doctor', this is not my area of expertise. So I am unsure whether INDY 10 is searching for 'inet:' and/or 'inet address:' with the colon at the end. If it is that would explain why it isn't working. I will further investigate.
GStack.GetLocalAddressList()
is implemented for OSX, so I couldn't say why it is not returning any IP addresses. You will just have to step into Indy's source code with the debugger and find out where the failure is actually occurring. Internally, it should be calling the OSX getifaddrs()
funding to get the IP addresses from the OS. Maybe that function is not working in your VM.
On a side note, your code is not very exception-safe. Things like IncUsage/DecUsage
, Create/Free
, etc should be wrapped in individual try/finally
blocks, not the way you are doing it (ie, an exception before IncUsage
causes DecUsage
to be called, unbalancing the refcount) . And you are leaking memory.
Your code should look more like this instead:
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;