Delphi,如何获取所有本地IP地址?

19

有人知道在Delphi中获取本地IP地址的简单列表(例如tstrings)的方法吗?

我已经查看了其他相关问题,但好像无法将它们转换为Delphi。


如果您能链接到已经工作的解决方案,那将会很有帮助。您不是在询问如何获取IP地址列表;而是在询问如何将其他代码转换为Delphi。因此,请展示原始代码。 - Rob Kennedy
这很棘手,最好取决于代码应该在哪些操作系统上运行...它可能存在缓冲区溢出/崩溃的风险。幸运的是,较晚的 Windows 操作系统至少有更好的 API/支持来解决这个问题。 - oOo
6个回答

15

在indy 9中,有一个名为IdStack的单元,其中包含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;

效果很好 :)

来自Remy Lebeau的评论

Indy 10 中也存在相同的功能,但代码略有不同:

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

2
在Indy 10中也存在相同的功能,但代码略有不同:TIdStack.IncUsage; try GStack.AddLocalAddressesToList(ComboBox1.Items); Edit.Caption := ComboBox1.Items[0]; finally TIdStack.DecUsage; end; - Remy Lebeau
@RemyLebeau 我正在尝试在XE2中使用Indy 10,但无法成功(同时使用'TStringList'和'TStrings' - 当调用'AddLocalAddressesToList(L);'时我得到“抽象错误”)。 - Jerry Dodge
奇怪的是,该过程在声明时是抽象的:procedure AddLocalAddressesToList(AAddresses: TStrings); virtual; abstract; - Jerry Dodge
2
TIdStack 中,它是抽象的,实现在派生类中。您不应该直接实例化 TIdStack。静态的 TIdStack.IncUsage() 方法会在需要时创建适当的实例,并将全局变量 GStack 分配给它。大多数对 TIdStack 的访问都需要通过 GStack 变量进行。 - Remy Lebeau
为什么结果显示了4行?其中1行是清晰的IPv4 IP地址,其他行似乎是IPv6地址。 - JO SeongGng

8

如果您正在使用ICS进行套接字通信,则可以使用OverbyteIcsWSocket单元中定义的LocalIPList函数。

即使您不使用它,也可以下载源代码并查找实现。它在内部使用WinSock。


谢谢,你给了我需要的提示,我正在使用Indy9,所以我在那里四处查找并找到了答案 :) - Christopher Chase

5
我在 Stack Overflow 上发布了一个解决方案,链接在这里。该方案将为系统上的每个适配器填充一个记录数组,其中包括 IP 地址,还包括 MAC 地址、子网掩码、传输/接收的数据包、描述等信息。

一旦您填充了记录数组,只需枚举它以获取 IP 地址,如果这是您想要的全部内容。

这相当于 Linux 上的 "ifconfig -a" 命令:

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

以下是“ifconfig -a”项目的完整源代码。您还需要获取我的辅助单元(uAdapterInfo)并将其与此程序一起包含。

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.

以下是您需要包含的辅助单元:

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.

看起来你上面的代码不完整。你链接的答案有大约两倍的代码量。 - Jerry Dodge
@Mick - 你的代码在这一行崩溃了(范围检查错误):for k := 1 to pIpTable^.dwNumEntries do - Gabriel
这行代码引发了EAccess Violation错误:tmp := tmp + '-' + format('%.2x',[pIfTable ^ .ifRow [i] .bPhysAddr [j]]),因为j是从dwPhysAddrLen派生的,而该值为3211316,而数组bPhysAddr仅有0-7个单元格。 - Hamish_Fernsby
一位评论者通过电子邮件向我提供了一个修复方案(我已在此处更新):“在类型“MIB_IFROW”中,将“wszName”和“bDescr”的声明从“char”更改为“ansichar”,它可以在Delphi 10.1上的Win 10上正常工作。” - Mick

4
绝地编码库包含一个过程。
procedure GetIpAddresses(Results: TStrings); 

在单元JclSysInfo中


2

还可以使用WinApi来实现(必要的头文件在Jedi ApiLib中)。这是我在我的TSAdminEx应用程序中的做法:

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;

1

从 Delphi 7 Indy 9 源代码中,我找到了使用 GStack 解决问题的方法。这与之前的帖子有些不同。

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;

我认为这也适用于Indy 10。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接