2011. február 5., szombat
Getting the IP address and mask for ALL TCP/IP interfaces
Problem/Question/Abstract:
We have seen a lot of methods obtaining the IP address of a machine. This is the "correct" method listing all addresses, network masks, broadcast addresses and status for all interfaces, including the loopback 127.0.0.1 - Requires WinSock 2
Answer:
This is a complete Delphi unit. By adding it to a project you can call :
EnumInterfaces(var s string): Boolean;
that returns a CRLF separated string of all IP addresses, netmasks, broadcast addresses and interface status.
unit USock;
interface
uses Windows, Winsock;
{
This function enumerates all TCP/IP interfaces and
returns a CRLF separated string containing:
IP, NetMask, BroadCast-Address, Up/Down status,
Broadcast support, Loopback
If you feed this string to a wide TMEMO (to its memo.lines.text
property) you will see cleary the results.
To use this you need Win98/ME/2K, 95 OSR 2 or NT service
pack #3 because WinSock 2 is used (WS2_32.DLL)
}
function EnumInterfaces(var sInt: string): Boolean;
{ Imported function WSAIOCtl from Winsock 2.0 - Winsock 2 is }
{ available only in Win98/ME/2K and 95 OSR2, NT srv pack #3 }
function WSAIoctl(s: TSocket; cmd: DWORD; lpInBuffer: PCHAR; dwInBufferLen:
DWORD;
lpOutBuffer: PCHAR; dwOutBufferLen: DWORD;
lpdwOutBytesReturned: LPDWORD;
lpOverLapped: POINTER;
lpOverLappedRoutine: POINTER): Integer; stdcall; external 'WS2_32.DLL';
{ Constants taken from C header files }
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;
filler: packed array[0..7] of char;
end;
type
INTERFACE_INFO = packed record
iiFlags: u_long; // Interface flags
iiAddress: sockaddr_gen; // Interface address
iiBroadcastAddress: sockaddr_gen; // Broadcast address
iiNetmask: sockaddr_gen; // Network mask
end;
implementation
{-------------------------------------------------------------------
1. Open WINSOCK
2. Create a socket
3. Call WSAIOCtl to obtain network interfaces
4. For every interface, get IP, MASK, BROADCAST, status
5. Fill a CRLF separated string with this info
6. Finito
--------------------------------------------------------------------}
function EnumInterfaces(var sInt: string): Boolean;
var
s: TSocket;
wsaD: WSADATA;
NumInterfaces: Integer;
BytesReturned, SetFlags: u_long;
pAddrInet: SOCKADDR_IN;
pAddrString: PCHAR;
PtrA: pointer;
Buffer: array[0..20] of INTERFACE_INFO;
i: Integer;
begin
result := true; // Initialize
sInt := '';
WSAStartup($0101, wsaD); // Start WinSock
// You should normally check
// for errors here :)
s := Socket(AF_INET, SOCK_STREAM, 0); // Open a socket
if (s = INVALID_SOCKET) then
exit;
try // Call WSAIoCtl
PtrA := @bytesReturned;
if (WSAIoCtl(s, SIO_GET_INTERFACE_LIST, nil, 0, @Buffer, 1024, PtrA, nil,
nil)
<> SOCKET_ERROR) then
begin // If ok, find out how
// many interfaces exist
NumInterfaces := BytesReturned div SizeOf(INTERFACE_INFO);
for i := 0 to NumInterfaces - 1 do // For every interface
begin
pAddrInet := Buffer[i].iiAddress.addressIn; // IP ADDRESS
pAddrString := inet_ntoa(pAddrInet.sin_addr);
sInt := sInt + ' IP=' + pAddrString + ',';
pAddrInet := Buffer[i].iiNetMask.addressIn; // SUBNET MASK
pAddrString := inet_ntoa(pAddrInet.sin_addr);
sInt := sInt + ' Mask=' + pAddrString + ',';
pAddrInet := Buffer[i].iiBroadCastAddress.addressIn; // Broadcast addr
pAddrString := inet_ntoa(pAddrInet.sin_addr);
sInt := sInt + ' Broadcast=' + pAddrString + ',';
SetFlags := Buffer[i].iiFlags;
if (SetFlags and IFF_UP) = IFF_UP then
sInt := sInt + ' Interface UP,' // Interface up/down
else
sInt := sInt + ' Interface DOWN,';
if (SetFlags and IFF_BROADCAST) = IFF_BROADCAST then // Broadcasts
sInt := sInt + ' Broadcasts supported,' // supported or
else // not supported
sInt := sInt + ' Broadcasts NOT supported,';
if (SetFlags and IFF_LOOPBACK) = IFF_LOOPBACK then // Loopback or
sInt := sInt + ' Loopback interface'
else
sInt := sInt + ' Network interface'; // normal
sInt := sInt + #13#10; // CRLF between
// each interface
end;
end;
except
end;
//
// Close sockets
//
CloseSocket(s);
WSACleanUp;
result := false;
end;
end.
here's a step-by-step on how to use it:
1. Copy/Paste the code to a text file and name it USock.pas (it's important that you name it like that, otherwise it won't work)
2. Open (Run) Delphi
3. Add a Memo control to the page and a button
4. Click on File/Save All and save the project somewhere (make a folder for it)
5. Put USock.pas in that folder
6. In Delphi, click on Project menu item and choose Add to Project
7. Select the USock.pas file and click on Open
8. NOW, doubleclick on the button you added to your project and write this:
in VAR section, write:
s: string
After begin (the main part) write:
EnumInterfaces(s);
memo1.lines.text := s;
So, the final procedure will look like:
procedure TForm1.Button1Click(Sender: TObject);
var
s: string;
begin
EnumInterfaces(s);
memo1.lines.text := s;
end;
9. Now, go to the top of your unit to the USES section and add USock to other uses things there, it will look like:
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls,
USock;
Then, run the project and click on that button. You will see the info in your memo box...
Here's what i get (the second part of it):
IP=127.0.0.1, Mask=255.0.0.0, Broadcast=255.255.255.255, Interface UP, Broadcasts supported, Loopback interface
So, as you see, all that info is in "s" variable...
Feliratkozás:
Megjegyzések küldése (Atom)
Your declaration of sockaddr_gen is wrong. It's a C union (aka a Delphi record with variant...) and you're "filler" using the "char" type will obviosly break in Unicode Delphi (where char is 16 bit wide). If you want a 8 byte filler, use the Byte type explicitly, not char, Delphi is not C...
VálaszTörlés