我在Windows XP sp3上使用 Delphi XE 2 Update 4
我正在寻找从安装的网络适配器,特别是广播IP获得最大可能的信息。
为此,我使用了Jan Schulz的代码,得到了from this article。
单位:
Unit USock;
Interface
Uses Windows, Winsock;
{ Unit to identify the network interfaces
This code requires at least Win98/ME/2K, 95 OSR 2 or NT service pack #3
as WinSock 2 is used (WS2_32.DLL) }
// Constants found in manual on non-officially documented M$ Winsock functions
Const SIO_GET_INTERFACE_LIST = $4004747F;
IFF_UP = $00000001;
IFF_BROADCAST = $00000002;
IFF_LOOPBACK = $00000004;
IFF_POINTTOPOINT = $00000008;
IFF_MULTICAST = $00000010;
Type SockAddr_Gen = Packed Record
AddressIn : SockAddr_In;
Padding : Packed Array [0..7] of Char;
end;
Interface_Info = Record
iiFlags : u_Long;
iiAddress : SockAddr_Gen;
iiBroadcastAddress : SockAddr_Gen;
iiNetmask : SockAddr_Gen;
end;
tNetworkInterface = Record
ComputerName : String;
AddrIP : String;
SubnetMask : String;
AddrNet : String;
AddrLimitedBroadcast : String;
AddrDirectedBroadcast : String;
IsInterfaceUp : Boolean;
BroadcastSupport : Boolean;
IsLoopback : Boolean;
end;
tNetworkInterfaceList = Array of tNetworkInterface;
Function WSAIoctl (aSocket : TSocket;
aCommand : DWord;
lpInBuffer : PChar;
dwInBufferLen : DWord;
lpOutBuffer : PChar;
dwOutBufferLen : DWord;
lpdwOutBytesReturned : LPDWord;
lpOverLapped : Pointer;
lpOverLappedRoutine : Pointer) : Integer; stdcall; external 'WS2_32.DLL';
Function GetNetworkInterfaces (Var aNetworkInterfaceList : tNetworkInterfaceList): Boolean;
Implementation
Function GetNetworkInterfaces (Var aNetworkInterfaceList : tNetworkInterfaceList): Boolean;
// Returns a complete list the of available network interfaces on a system (IPv4)
// Copyright by Dr. Jan Schulz, 23-26th March 2007
// This version can be used for free and non-profit projects. In any other case get in contact
// Written with information retrieved from MSDN
// www.code10.net
Var aSocket : TSocket;
aWSADataRecord : WSAData;
NoOfInterfaces : Integer;
NoOfBytesReturned : u_Long;
InterfaceFlags : u_Long;
NameLength : DWord;
pAddrIP : SockAddr_In;
pAddrSubnetMask : SockAddr_In;
pAddrBroadcast : Sockaddr_In;
pIPString : PChar;
pSubnetMaskString : PChar;
pLimBroadcastString : PChar;
pNetAddrString : PChar;
pDirBroadcastString : PChar;
DirBroadcastDummy : In_Addr;
NetAddrDummy : In_Addr;
Buffer : Array [0..30] of Interface_Info;
i : Integer;
Begin
Result := False;
SetLength (aNetworkInterfaceList, 0);
// Startup of old the WinSock
// WSAStartup ($0101, aWSADataRecord);
// Startup of WinSock2
WSAStartup(MAKEWORD(2, 0), aWSADataRecord);
// Open a socket
aSocket := Socket (AF_INET, SOCK_STREAM, 0);
// If impossible to open a socket, not worthy to go any further
If (aSocket = INVALID_SOCKET) THen Exit;
Try
If WSAIoCtl (aSocket, SIO_GET_INTERFACE_LIST, NIL, 0,
@Buffer, 1024, @NoOfBytesReturned, NIL, NIL) <> SOCKET_ERROR THen
Begin
NoOfInterfaces := NoOfBytesReturned Div SizeOf (Interface_Info);
SetLength (aNetworkInterfaceList, NoOfInterfaces);
// For each of the identified interfaces get:
For i := 0 to NoOfInterfaces - 1 do
Begin
With aNetworkInterfaceList[i] do
Begin
// Get the name of the machine
NameLength := MAX_COMPUTERNAME_LENGTH + 1;
SetLength (ComputerName, NameLength) ;
If Not Windows.GetComputerName (PChar (Computername), NameLength) THen ComputerName := '';
// Get the IP address
pAddrIP := Buffer[i].iiAddress.AddressIn;
pIPString := inet_ntoa (pAddrIP.Sin_Addr);
AddrIP := pIPString;
// Get the subnet mask
pAddrSubnetMask := Buffer[i].iiNetMask.AddressIn;
pSubnetMaskString := inet_ntoa (pAddrSubnetMask.Sin_Addr);
SubnetMask := pSubnetMaskString;
// Get the limited broadcast address
pAddrBroadcast := Buffer[i].iiBroadCastAddress.AddressIn;
pLimBroadcastString := inet_ntoa (pAddrBroadcast.Sin_Addr);
AddrLimitedBroadcast := pLimBroadcastString;
// Calculate the net and the directed broadcast address
NetAddrDummy.S_addr := Buffer[i].iiAddress.AddressIn.Sin_Addr.S_Addr;
NetAddrDummy.S_addr := NetAddrDummy.S_addr And Buffer[i].iiNetMask.AddressIn.Sin_Addr.S_Addr;
DirBroadcastDummy.S_addr := NetAddrDummy.S_addr Or Not Buffer[i].iiNetMask.AddressIn.Sin_Addr.S_Addr;
pNetAddrString := inet_ntoa ((NetAddrDummy));
AddrNet := pNetAddrString;
pDirBroadcastString := inet_ntoa ((DirBroadcastDummy));
AddrDirectedBroadcast := pDirBroadcastString;
// From the evaluation of the Flags we receive more information
InterfaceFlags := Buffer[i].iiFlags;
// Is the network interface up or down ?
If (InterfaceFlags And IFF_UP) = IFF_UP THen IsInterfaceUp := True
Else IsInterfaceUp := False;
// Does the network interface support limited broadcasts ?
If (InterfaceFlags And IFF_BROADCAST) = IFF_BROADCAST THen BroadcastSupport := True
Else BroadcastSupport := False;
// Is the network interface a loopback interface ?
If (InterfaceFlags And IFF_LOOPBACK) = IFF_LOOPBACK THen IsLoopback := True
Else IsLoopback := False;
end;
end;
end;
Except
Result := False;
end;
// Cleanup the mess
CloseSocket (aSocket);
WSACleanUp;
Result := True;
end;
end.
呼叫示例:
uses USock;
Procedure TForm1.Button1Click(Sender: TObject);
Var i : Integer;
aNetInterfaceList : tNetworkInterfaceList;
Begin
If (GetNetworkInterfaces (aNetInterfaceList)) THen
Begin
Memo1.Clear;
Memo1.Lines.Add (DateTimeToStr (Now)+ ' : ');
For i := 0 to High (aNetInterfaceList) do
Begin
Memo1.Lines.Add ('');
Memo1.Lines.Add ('# : ' + IntToStr(i));
Memo1.Lines.Add ('Name : ' + aNetInterfaceList[i].ComputerName);
Memo1.Lines.Add ('IP-Address : ' + aNetInterfaceList[i].AddrIP);
Memo1.Lines.Add ('Subnet mask : ' + aNetInterfaceList[i].SubnetMask);
Memo1.Lines.Add ('Net address : ' + aNetInterfaceList[i].AddrNet);
Memo1.Lines.Add ('Limited broadcast address : ' + aNetInterfaceList[i].AddrLimitedBroadcast);
Memo1.Lines.Add ('Directed Broadcast address : ' + aNetInterfaceList[i].AddrDirectedBroadcast);
Memo1.Lines.Add ('Interface up : ' + BoolToStr (aNetInterfaceList[i].IsInterfaceUp, True));
Memo1.Lines.Add ('Broadcast supported : ' + BoolToStr (aNetInterfaceList[i].BroadcastSupport, True));
Memo1.Lines.Add ('Loopback interface : ' + BoolToStr (aNetInterfaceList[i].IsLoopback, True));
Memo1.Lines.Add ('');
end;
end;
end;
这段代码显然可以工作,但是它只返回一个网络接口,即环回接口(127.0.0.0),而且它还应该返回我的网络专用接口。
在这部分代码中,它总是只返回一个可用的接口:
接口数:=返回的字节数Div大小(接口信息);
为了在XE 2上工作,我必须更改所使用的字符串(AnsiString)。
我也尝试过使用Winsock 2单元,也尝试过使用IdWinsock 2和那里的API调用。
在所有情况下,API都能正常工作,并且只返回环回接口。
使用另一个在 Delphi 上编写的实用程序,我可以得到一个这个列表,本地IP 192.168.0.112也被列出了,但是这个源代码并不容易使用。
我的问题是:出什么事了?
6条答案
按热度按时间s4chpxco1#
在雷米Lebeau的建议和帮助下,我在 Delphi 中找到了这个源代码,用XP和W7进行了测试,它使用GetAdaptersInfo()提供了信息。
感谢布拉德·普伦德加斯特original post由马库斯·胡姆更新final version
我添加了子网掩码报告功能,以便让像我这样的新手清楚地了解信息的存储位置:
bcs8qyzn2#
Jan Schulz的代码在正确转换为Unicode友好的 Delphi 后可以正常工作。
我做了一些修正:
char
必须转换为AnsiChar
。在本例中,这只发生在SockAddr_Gen
记录的Padding
字段中,它不仅会扭曲记录,而且会导致SizeOf(Interface_Info)
以及随后的NoOfInterfaces
返回错误的结果。由于它实际上不是一个字符,最好将其定义为byte
。inet_ntoa
调用结果的PChars
,并直接为TNetworkInterface
字符串字段赋值。TNetworkInterface
中的Strings
是正确的,因为它们不会传递给任何api呼叫。此外,ComputerName
会传递给GetComputerName
api,其预期为PWideChar
/PChar
。3bygqnnd3#
不管怎样,如果您需要特定适配器的广播IP,可以使用
SIO_GET_BROADCAST_ADDRESS
代替。也就是说,非Winsock的解决方案是使用
GetAdaptersInfo()
或GetAdaptersAddresses()
。这样,您就不必创建SOCKET
来获取信息,您可以同时枚举IPv4和IPv6适配器,以及Winsock无法识别的其他适配器。对于
GetAdaptersInfo()
,IP_ADAPTER_INFO.IpAddressList
列表包含IPv4 IP和子网掩码(在XP+上,单向适配器包含在输出中,但您可以使用GetUniDirectionalAdapterInfo()
将其过滤掉)。对于
GetAdaptersAddresses()
,IP_ADAPTER_ADDRESSES.FirstUnicastAddress
列表包含IPv4和IPv6 IP,以及Vista+上的IPv4子网掩码。对于XP和更早版本,您可以使用GetIpAddrTable()
检索IPv4子网掩码,并将它们与GetAdaptersAddresses()
中的IPv4 IP匹配。一旦您有了IPv4 IP和子网掩码,计算其广播IP就非常简单:
utugiqy64#
由于您已将String声明更改为AnsiString,因此也要将Char声明更改为AnsiChar。
8dtrkrch5#
支持 Delphi 10.2 Tokyo的更新。
lh80um4z6#
已更新,修复了循环链接列表时覆盖pAdapterInfo指针的问题
;