Delphi, How to get all local IPs?

2019-01-11 14:00发布

问题:

Any one know a way in delphi get a simple list (eg tstrings) of the local ip address.

I have had a look at the other related question, and cant seem to get my head around converting them to delphi.

回答1:

in indy 9, there is a unit IdStack, with the class TIdStack

fStack := TIdStack.CreateStack;
try
  edit.caption := fStack.LocalAddress;  //the first address i believe
  ComboBox1.Items.Assign(fStack.LocalAddresses); //all the address'
finally
  freeandnil(fStack); 
end;

works great :)

from Remy Lebeau's Comment

The same exists in Indy 10, but the code is a little different:

TIdStack.IncUsage; 
try 
  GStack.AddLocalAddressesToList(ComboBox1.Items); 
  Edit.Caption := ComboBox1.Items[0]; 
finally 
  TIdStack.DecUsage; 
end; 


回答2:

If you are using ICS for socket communication, you can use LocalIPList function, defined in the OverbyteIcsWSocket unit.

Even if you are not using it, you can download the source code and look up the implementation. It uses WinSock internally.



回答3:

The Jedi Code Library contains a procedure

procedure GetIpAddresses(Results: TStrings); 

in unit JclSysInfo



回答4:

I posted a solution on SO here. This will populate an array of records full of information for every adapter on the system. This includes the IP Address, but also includes MAC address, subnet mask, transferred/recvd packets, description, etc.

Once you've populated the array of records, just enumerate it for the IP Addresses, if that's all you want.

This emulates "ifconfig -a" on Linux:

C:\>ifconfig
0x00000001
"MS TCP Loopback interface"
        Link encap: Local loopback
        inet addr:127.0.0.1 Mask: 255.0.0.0
        MTU: 1520 Speed:10.00 Mbps
        Admin status:UP Oper status:OPERATIONAL
        RX packets:179805 dropped:0 errors:0 unkown:0
        TX packets:179804 dropped:0 errors:0 txqueuelen:0

0x00000002
"Broadcom NetXtreme 57xx Gigabit Controller - Packet Scheduler Miniport"
        Link encap: Ethernet HWaddr: XX-XX-XX-XX-XX-XX
        inet addr:10.101.101.102 Mask: 255.255.255.0
        MTU: 1500 Speed:100.00 Mbps
        Admin status:UP Oper status:OPERATIONAL
        RX packets:6287896 dropped:0 errors:0 unkown:0
        TX packets:5337100 dropped:0 errors:1 txqueuelen:0

Here is the full source of the "ifconfig -a" project. You will also need to grab my helper unit (uAdapterInfo) and include it with this program.

program ifconfig;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Classes,
  Winsock,
  uAdapterInfo in 'uAdapterInfo.pas';

type
  TAdapterInfo = array of record
    dwIndex:    longint;
    dwType:     longint;
    dwMtu:      longint;
    dwSpeed:    extended;
    dwPhysAddrLen: longint;
    bPhysAddr:  string;
    dwAdminStatus: longint;
    dwOperStatus: longint;
    dwLastChange: longint;
    dwInOctets: longint;
    dwInUcastPkts: longint;
    dwInNUcastPkts: longint;
    dwInDiscards: longint;
    dwInErrors: longint;
    dwInUnknownProtos: longint;
    dwOutOctets: longint;
    dwOutUcastPkts: longint;
    dwOutNUcastPkts: longint;
    dwOutDiscards: longint;
    dwOutErrors: longint;
    dwOutQLen:  longint;
    dwDescrLen: longint;
    bDescr:     string;
    sIpAddress: string;
    sIpMask:    string;
  end;




  function Get_EthernetAdapterDetail(var AdapterDataFound: TAdapterInfo): boolean;
  var
    pIfTable: ^_IfTable;
    pIpTable: ^_IpAddrTable;
    ifTableSize, ipTableSize: longint;
    tmp:      string;
    i, j, k, m: integer;
    ErrCode:  longint;
    sAddr, sMask: in_addr;
    IPAddresses, IPMasks: TStringList;
    sIPAddressLine, sIPMaskLine: string;
    bResult:  boolean;
  begin
    bResult  := True; //default return value
    pIfTable := nil;
    pIpTable := nil;

    IPAddresses := TStringList.Create;
    IPMasks     := TStringList.Create;

    try
      // First: just get the buffer size.
      // TableSize returns the size needed.
      ifTableSize := 0; // Set to zero so the GetIfTabel function
      // won't try to fill the buffer yet, 
      // but only return the actual size it needs.
      GetIfTable(pIfTable, ifTableSize, 1);
      if (ifTableSize < SizeOf(MIB_IFROW) + Sizeof(longint)) then
      begin
        bResult := False;
        Result := bResult;
        Exit; // less than 1 table entry?!
      end;

      ipTableSize := 0;
      GetIpAddrTable(pIpTable, ipTableSize, 1);
      if (ipTableSize < SizeOf(MIB_IPADDRROW) + Sizeof(longint)) then
      begin
        bResult := False;
        Result := bResult;
        Exit; // less than 1 table entry?!
      end;

      // Second:
      // allocate memory for the buffer and retrieve the 
      // entire table.
      GetMem(pIfTable, ifTableSize);
      ErrCode := GetIfTable(pIfTable, ifTableSize, 1);

      if ErrCode <> ERROR_SUCCESS then
      begin
        bResult := False;
        Result := bResult;
        Exit; // OK, that did not work. 
        // Not enough memory i guess.
      end;

      GetMem(pIpTable, ipTableSize);
      ErrCode := GetIpAddrTable(pIpTable, ipTableSize, 1);

      if ErrCode <> ERROR_SUCCESS then
      begin
        bResult := False;
        Result := bResult;
        Exit;
      end;

      for k := 1 to pIpTable^.dwNumEntries do
      begin
        sAddr.S_addr := pIpTable^.table[k].dwAddr;
        sMask.S_addr := pIpTable^.table[k].dwMask;

        sIPAddressLine := Format('0x%8.8x', [(pIpTable^.table[k].dwIndex)]) +
          '=' + Format('%s', [inet_ntoa(sAddr)]);
        sIPMaskLine    := Format('0x%8.8x', [(pIpTable^.table[k].dwIndex)]) +
          '=' + Format('%s', [inet_ntoa(sMask)]);

        IPAddresses.Add(sIPAddressLine);
        IPMasks.Add(sIPMaskLine);
      end;

      SetLength(AdapterDataFound, pIfTable^.nRows); //initialize the array or records
      for i := 1 to pIfTable^.nRows do
        try
          //if pIfTable^.ifRow[i].dwType=MIB_IF_TYPE_ETHERNET then
          //begin
          m := i - 1;
          AdapterDataFound[m].dwIndex := 4;//(pIfTable^.ifRow[i].dwIndex);
          AdapterDataFound[m].dwType := (pIfTable^.ifRow[i].dwType);
          AdapterDataFound[m].dwIndex := (pIfTable^.ifRow[i].dwIndex);
          AdapterDataFound[m].sIpAddress :=
            IPAddresses.Values[Format('0x%8.8x', [(pIfTable^.ifRow[i].dwIndex)])];
          AdapterDataFound[m].sIpMask :=
            IPMasks.Values[Format('0x%8.8x', [(pIfTable^.ifRow[i].dwIndex)])];
          AdapterDataFound[m].dwMtu := (pIfTable^.ifRow[i].dwMtu);
          AdapterDataFound[m].dwSpeed := (pIfTable^.ifRow[i].dwSpeed);
          AdapterDataFound[m].dwAdminStatus := (pIfTable^.ifRow[i].dwAdminStatus);
          AdapterDataFound[m].dwOperStatus := (pIfTable^.ifRow[i].dwOperStatus);
          AdapterDataFound[m].dwInUcastPkts := (pIfTable^.ifRow[i].dwInUcastPkts);
          AdapterDataFound[m].dwInNUcastPkts := (pIfTable^.ifRow[i].dwInNUcastPkts);
          AdapterDataFound[m].dwInDiscards := (pIfTable^.ifRow[i].dwInDiscards);
          AdapterDataFound[m].dwInErrors := (pIfTable^.ifRow[i].dwInErrors);
          AdapterDataFound[m].dwInUnknownProtos := (pIfTable^.ifRow[i].dwInUnknownProtos);
          AdapterDataFound[m].dwOutNUcastPkts := (pIfTable^.ifRow[i].dwOutNUcastPkts);
          AdapterDataFound[m].dwOutUcastPkts := (pIfTable^.ifRow[i].dwOutUcastPkts);
          AdapterDataFound[m].dwOutDiscards := (pIfTable^.ifRow[i].dwOutDiscards);
          AdapterDataFound[m].dwOutErrors := (pIfTable^.ifRow[i].dwOutErrors);
          AdapterDataFound[m].dwOutQLen := (pIfTable^.ifRow[i].dwOutQLen);
          AdapterDataFound[m].bDescr := (pIfTable^.ifRow[i].bDescr);

          tmp := '';
          for j := 0 to pIfTable^.ifRow[i].dwPhysAddrLen - 1 do
          begin
            if Length(tmp) > 0 then
              tmp := tmp + '-' + format('%.2x', [pIfTable^.ifRow[i].bPhysAddr[j]])
            else
              tmp := tmp + format('%.2x', [pIfTable^.ifRow[i].bPhysAddr[j]]);
          end;

          if Length(tmp) > 0 then
          begin
            AdapterDataFound[m].bPhysAddr := tmp;
          end;
        except
          bResult := False;
          Result := bResult;
          Exit;
        end;
    finally
      if Assigned(pIfTable) then
      begin
        FreeMem(pIfTable, ifTableSize);
      end;

      FreeAndNil(IPMasks);
      FreeAndNil(IPAddresses);
    end;

    Result := bResult;
  end;



var
  AdapterData: TAdapterInfo;
  i: integer;
begin
  try
    WriteLn('');
    if Get_EthernetAdapterDetail(AdapterData) then
    begin
      for i := 0 to Length(AdapterData) - 1 do
      begin
        WriteLn(Format('0x%8.8x', [AdapterData[i].dwIndex]));
        WriteLn('"' + AdapterData[i].bDescr + '"');
        Write(Format(#9 + 'Link encap: %s ', [Get_if_type(AdapterData[i].dwType)]));

        if Length(AdapterData[i].bPhysAddr) > 0 then
          Write('HWaddr: ' + AdapterData[i].bPhysAddr);

        Write(#13 + #10 + #9 + 'inet addr:' + AdapterData[i].sIpAddress);
        WriteLn(' Mask: ' + AdapterData[i].sIpMask);
        WriteLn(Format(#9 + 'MTU: %d Speed:%.2f Mbps', [AdapterData[i].dwMtu,
          (AdapterData[i].dwSpeed) / 1000 / 1000]));
        Write(#9 + 'Admin status:' + Get_if_admin_status(AdapterData[i].dwAdminStatus));
        WriteLn(' Oper status:' + Get_if_oper_status(AdapterData[i].dwOperStatus));
        WriteLn(#9 + Format('RX packets:%d dropped:%d errors:%d unkown:%d',
          [AdapterData[i].dwInUcastPkts + AdapterData[i].dwInNUcastPkts,
          AdapterData[i].dwInDiscards, AdapterData[i].dwInErrors,
          AdapterData[i].dwInUnknownProtos]));
        WriteLn(#9 + Format('TX packets:%d dropped:%d errors:%d txqueuelen:%d',
          [AdapterData[i].dwOutUcastPkts + AdapterData[i].dwOutNUcastPkts,
          AdapterData[i].dwOutDiscards, AdapterData[i].dwOutErrors,
          AdapterData[i].dwOutQLen]));

        WriteLn('');
      end;
    end
    else
    begin
      WriteLn(#13+#10+'*** Error retrieving adapter information');
    end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

And here is the helper unit you need to include:

unit uAdapterInfo;

interface

uses
  Classes,
  SysUtils;

const
  MAX_INTERFACE_NAME_LEN = $100;
  ERROR_SUCCESS   = 0;
  MAXLEN_IFDESCR  = $100;
  MAXLEN_PHYSADDR = 8;

  MIB_IF_OPER_STATUS_NON_OPERATIONAL = 0;
  MIB_IF_OPER_STATUS_UNREACHABLE = 1;
  MIB_IF_OPER_STATUS_DISCONNECTED = 2;
  MIB_IF_OPER_STATUS_CONNECTING  = 3;
  MIB_IF_OPER_STATUS_CONNECTED   = 4;
  MIB_IF_OPER_STATUS_OPERATIONAL = 5;

  MIB_IF_TYPE_OTHER    = 1;
  MIB_IF_TYPE_ETHERNET = 6;
  MIB_IF_TYPE_TOKENRING = 9;
  MIB_IF_TYPE_FDDI     = 15;
  MIB_IF_TYPE_PPP      = 23;
  MIB_IF_TYPE_LOOPBACK = 24;
  MIB_IF_TYPE_SLIP     = 28;

  MIB_IF_ADMIN_STATUS_UP      = 1;
  MIB_IF_ADMIN_STATUS_DOWN    = 2;
  MIB_IF_ADMIN_STATUS_TESTING = 3;

  _MAX_ROWS_ = 20;
  ANY_SIZE   = 1;


type
  MIB_IFROW = record
    wszName:    array[0 .. (MAX_INTERFACE_NAME_LEN * 2 - 1)] of ansichar;
    dwIndex:    longint;
    dwType:     longint;
    dwMtu:      longint;
    dwSpeed:    longint;
    dwPhysAddrLen: longint;
    bPhysAddr:  array[0 .. (MAXLEN_PHYSADDR - 1)] of byte;
    dwAdminStatus: longint;
    dwOperStatus: longint;
    dwLastChange: longint;
    dwInOctets: longint;
    dwInUcastPkts: longint;
    dwInNUcastPkts: longint;
    dwInDiscards: longint;
    dwInErrors: longint;
    dwInUnknownProtos: longint;
    dwOutOctets: longint;
    dwOutUcastPkts: longint;
    dwOutNUcastPkts: longint;
    dwOutDiscards: longint;
    dwOutErrors: longint;
    dwOutQLen:  longint;
    dwDescrLen: longint;
    bDescr:     array[0 .. (MAXLEN_IFDESCR - 1)] of ansichar;
  end;

type
  MIB_IPADDRROW = record
    dwAddr:      longint;
    dwIndex:     longint;
    dwMask:      longint;
    dwBCastAddr: longint;
    dwReasmSize: longint;
    unused1:     word;
    unused2:     word;
  end;

type
  _IfTable = record
    nRows: longint;
    ifRow: array[1.._MAX_ROWS_] of MIB_IFROW;
  end;

type
  _IpAddrTable = record
    dwNumEntries: longint;
    table: array[1..ANY_SIZE] of MIB_IPADDRROW;
  end;



function GetIfTable(pIfTable: Pointer; var pdwSize: longint; bOrder: longint): longint;
  stdcall;
function GetIpAddrTable(pIpAddrTable: Pointer; var pdwSize: longint;
  bOrder: longint): longint; stdcall;

function Get_if_type(iType: integer): string;
function Get_if_admin_status(iStatus: integer): string;
function Get_if_oper_status(iStatus: integer): string;


implementation

function GetIfTable; stdcall; external 'IPHLPAPI.DLL';
function GetIpAddrTable; stdcall; external 'IPHLPAPI.DLL';

function Get_if_type(iType: integer): string;
var
  sResult: string;
begin
  sResult := 'UNKNOWN';
  case iType of
    1: sResult   := 'Other';
    6: sResult   := 'Ethernet';
    9: sResult   := 'Tokenring';
    15: sResult  := 'FDDI';
    23: sResult  := 'PPP';
    24: sResult  := 'Local loopback';
    28: sResult  := 'SLIP';
    37: sResult  := 'ATM';
    71: sResult  := 'IEEE 802.11';
    131: sResult := 'Tunnel';
    144: sResult := 'IEEE 1394 (Firewire)';
  end;

  Result := sResult;
end;

function Get_if_admin_status(iStatus: integer): string;
var
  sResult: string;
begin
  sResult := 'UNKNOWN';

  case iStatus of
    1: sResult := 'UP';
    2: sResult := 'DOWN';
    3: sResult := 'TESTING';
  end;

  Result := sResult;
end;

function Get_if_oper_status(iStatus: integer): string;
var
  sResult: string;
begin
  sResult := 'UNKNOWN';

  case iStatus of
    0: sResult := 'NON_OPERATIONAL';
    1: sResult := 'UNREACHABLE';
    2: sResult := 'DISCONNECTED';
    3: sResult := 'CONNECTING';
    4: sResult := 'CONNECTED';
    5: sResult := 'OPERATIONAL';
  end;

  Result := sResult;
end;

end.


回答5:

It can also be done by using WinApi (necessary headers are in the Jedi ApiLib). This is how I do it in my TSAdminEx application:

function EnumerateIpAddresses(var IPList: TStringList): Boolean;
var
  IPAddrTable: PMIB_IPADDRTABLE;
  Size: DWORD;
  Res: DWORD;
  Index: Integer;
  Addr: IN_ADDR;
begin
  Result := False;

  IPList.Duplicates := dupIgnore;

  Size := 0;
  // Get required Size
  if GetIpAddrTable(nil, Size, False) <> ERROR_INSUFFICIENT_BUFFER then Exit;

  // Reserve mem
  GetMem(IPAddrTable, Size);
  Res := GetIpAddrTable(IPAddrTable, Size, True);

  if Res <> NO_ERROR then Exit;

  for Index := 0 to IPAddrTable^.dwNumEntries-1 do
  begin
    // Convert ADDR to String and add to IPList
    Addr.S_addr := IPAddrTable^.table[Index].dwAddr;
    // Prevent implicit string conversion warning in D2009 by explicit cast to string
    IPList.Add({$IFDEF UNICODE}String({$ENDIF UNICODE}inet_ntoa(Addr){$IFDEF UNICODE}){$ENDIF UNICODE});
  end;

  // Free Mem
  FreeMem(IPAddrTable);

  Result := True;
end;


回答6:

From Delphi 7 Indy 9 source I find something that solved the problem using GStack. It's a little bit different from previous post.

function GetLocalIPAddress(List: TStringlist): Integer;
begin     
  if Assigned(GStack) then
    List.Assign(TStringlist(GStack.LocalAddresses))
  else
  begin
    GStack := GStackClass.Create;
    List.Assing(TStringlist(GStack.LocalAddresses));
    FreeAndNil(GStack);
  end;
end;

I think this will work with Indy 10 too.