Solved

Delphi - How to Get MAC Address from Computer on LAN if I Know Its Name

Posted on 2014-01-08
15
4,734 Views
Last Modified: 2014-01-09
I know the name of another computer on the LAN. In Delphi (XE4), how can I obtain its MAC address?
0
Comment
Question by:plumothy
  • 5
  • 4
  • 3
  • +2
15 Comments
 
LVL 53

Expert Comment

by:Will Szymkowski
Comment Utility
Not sure about Delphi but you can use command prompt for this.
- open command prompt
- type arp -d
- ping <servername>
- type arp -a

This will show you the IP and MAC address of the machine you are pinging. If you are trying to get the MAC address of another computer that is on a different VLAN then you need to use a machine on the same VLAN. ARP broadcast do not go over different VLANS.

Will.
0
 

Author Comment

by:plumothy
Comment Utility
Thanks, but I already know that. It is a Delphi function that I am after.
0
 
LVL 36

Expert Comment

by:Geert Gruwez
Comment Utility
on about.com there is a procedure on how to do this
it uses the winsock unit to solve this

http://delphi.about.com/od/networking/l/aa103100a.htm
0
 

Author Comment

by:plumothy
Comment Utility
Thanks - but that's not what I am asking for. That solution gets me my own Host Name and IP Address.

I want to to get the MAC Address of another computer (ie not mine) on the same LAN. I already know the Host Name of that other computer.
0
 
LVL 25

Expert Comment

by:Sinisa Vuk
Comment Utility
Here you have complete function which use arp packets sent to destination computer using local net's computer name.

uses WinSock;

function SendARP(DestIp: DWORD; SrcIP: DWORD; pMacAddr: Pointer; PhyAddrLen: Pointer): DWORD;stdcall; external 'iphlpapi.dll';

function GetRemoteMacAddress(const sComputer: String; var sMacAddress: String): Boolean;
var
  WSAData : TWSAData;
  hostEnt : PHostEnt;
  sIPAddress: String;
  DestIP: Integer;
  MacAddr: array[0..5] of Byte;
  PhLen, i: Integer;
begin
  Result := False;
  sMacAddress := '';

  //initialize
  WSAStartup($101, WSAData);
  try
    //translate copmuter name to ip
    hostEnt := GetHostByName(PChar(sComputer));
    if Assigned(hostEnt) then
    begin
      if Assigned(hostEnt^.h_addr_list) then
      begin
        //if address is available?
        if Assigned(hostEnt^.h_addr_list^) then
        begin
          //format ip address
          sIPAddress := '';

          for i := 0 to hostEnt^.h_length - 1 do
            sIPAddress := Concat(sIPAddress, IntToStr(Ord(hostEnt^.h_addr_list^[i])) + '.');
          SetLength(sIPAddress, Length(sIPAddress) - 1); //trunc last point

          //translate ip address 
          DestIP := Inet_addr(PChar(sIPAddress));
          if DestIP <> INADDR_NONE then
          begin
            PhLen := SizeOf(MacAddr);
            ZeroMemory(@MacAddr[0], PhLen);
            //send arp packet and wait for response with mac address
            if SendARP(DestIP, 0, Pointer(@MacAddr[0]), @PhLen) = NO_ERROR then
            begin
              if (PhLen <> 0) then
              begin
                //format mac address
                for i := 0 to PhLen-1 do
                  sMacAddress := sMacAddress + IntToHex(MacAddr[i], 2) + '-';
                SetLength(sMacAddress, Length(sMacAddress) - 1); //trunc last -

                Result := True;
              end;
            end;
          end;
        end;
      end;
    end;
  finally
    WSACleanup;
  end;
end;

Open in new window


...should work with IPv4 and IPv6.
0
 
LVL 36

Expert Comment

by:Geert Gruwez
Comment Utility
oops, i forgot to add the changes
here's my test unit

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Edit1: TEdit;
    Memo1: TMemo;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    fResolvedIps: TStrings;
    function IpAddress(HostName: string): string;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;

var
  Form1: TForm1;

implementation

uses StrUtils, Winsock;

{$R *.dfm}

function GetIPFromHost
(var HostName, IPaddr, WSAErr: string): Boolean;
type
  Name = array[0..100] of AnsiChar;
  PName = ^Name;
var
  HEnt: pHostEnt;
  HName: PName;
  WSAData: TWSAData;
  i: Integer;
  N: PAnsiChar;
begin
  Result := False;
  if WSAStartup($0101, WSAData) <> 0 then begin
    WSAErr := 'Winsock is not responding."';
    Exit;
  end;
  IPaddr := '';
  New(HName);
  for I := 1 to Length(HostName) do
    Hname^[I-1] := AnsiChar(HostName[I]);
  HName^[Length(HostName)] := #0;
  HEnt := GetHostByName(HName^);
  for i := 0 to HEnt^.h_length - 1 do
   IPaddr :=
    Concat(IPaddr,
    IntToStr(Ord(HEnt^.h_addr_list^[i])) + '.');
  SetLength(IPaddr, Length(IPaddr) - 1);
  Result := True;
  Dispose(HName);
  WSACleanup;
end;

function TForm1.IpAddress(HostName: string): string;
var aHostName, aIPaddr, aWSAErr: string;
begin
  aHostName := HostName;
  aIpAddr := '';
  aWSAErr := '';
  if GetIPFromHost(aHostName, aIpAddr, aWSAErr) then
    fResolvedIps.Values[aHostName] := aIpAddr;
  Result := fResolvedIps.Values[HostName];
  // Workaround for non-found ip adress
  //if Result = fResolvedIps.Values['localhost'] then
    //Result := '';
end;

procedure TForm1.Button1Click(Sender: TObject);
var temp: string;
begin
  Temp := IpAddress(Edit1.Text);
  if Temp = '' then
    Temp := 'Not found';
  Memo1.Lines.Add(Format('Ip Adress for %S: %S', [Edit1.Text, Temp]));
end;

constructor TForm1.Create(AOwner: TComponent);
begin
  inherited Create(aOwner);
  fResolvedIps := TStringList.Create;
  IpAddress('localhost');
end;

destructor TForm1.Destroy;
begin
  fResolvedIps.Free;
  inherited Destroy;
end;

end.

Open in new window

0
 

Author Comment

by:plumothy
Comment Utility
Thank you guys.

sinisav: your solution does not work for me. When I run it, I get: WSANO_DATA, which MSDN tells me means: "The requested name is valid, but no data of the requested type was found. This error is also returned if the name parameter contains a string representation of an IPv6 address or an illegal IPv4 address.
This error should not be interpreted to mean that the name parameter contains a name string that has been validated for a particular protocol (an IP hostname, for example). Since Winsock supports multiple name service providers, a name may potentially be valid for one provider and not accepted by another provider."

I know that the host name I am using is correct and that it is on the LAN up and running.

Geert Gruwez: your solution runs OK and it works but it does not do what I want. It produces the host's IP address - I want its MAC address.
0
Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

 
LVL 25

Expert Comment

by:Sinisa Vuk
Comment Utility
Work for me in D6 on xp. I'll try in newest D. Maybe some stuff are changed.
0
 
LVL 36

Expert Comment

by:Geert Gruwez
Comment Utility
edited, wowa, misread the q ... my excuses
0
 
LVL 25

Expert Comment

by:Sinisa Vuk
Comment Utility
Fixed. Works in Win64 + Delphi XE3 + unicode. Won't work on XP. For XP is needed non-unicode version of structures and functions.

type
  PAddrInfo = ^TAddrInfo;
  TAddrInfo = packed record
    ai_flags: Integer;
    ai_family: Integer;
    ai_socktype: Integer;
    ai_protocol: Integer;
    ai_addrlen: LongWord;
    ai_canonname: Array of Char;
    ai_addr: PSockAddrIn;
    ai_next: PAddrInfo;
  end;

  PAddrInfoW = ^TAddrInfoW;
  TAddrInfoW = packed record
    ai_flags: integer;
    ai_family: integer;
    ai_socktype: integer;
    ai_protocol: integer;
    ai_addrlen: Cardinal;
    ai_canonname: PWideChar;
    ai_addr: PSockAddrIn;
    ai_next: PAddrInfo;
    end;

function SendARP(DestIp: DWORD; SrcIP: DWORD; pMacAddr: Pointer; PhyAddrLen: Pointer): DWORD;stdcall; external 'iphlpapi.dll';
function getaddrinfo(const nodename: PChar; const servname : PChar; const hints: PAddrInfo; var res: PAddrInfo): Integer; stdcall; external 'ws2_32.dll';
function GetAddrInfoW(nodename, servname: PWideChar; pHints: PAddrInfoW; out res: PAddrInfoW): integer; stdcall; external 'ws2_32.dll' name 'GetAddrInfoW';
procedure freeaddrinfo(ai: PAddrInfo); stdcall; external 'ws2_32.dll';
procedure FreeAddrInfoW(pai: PAddrInfoW); stdcall; external 'ws2_32.dll' name 'FreeAddrInfoW';

function GetRemoteMacAddressWin7(const sComputer: String; var sMacAddress: String): Boolean;
var
  WSAData : TWSAData;
  //sIPAddress: String;
  DestIP: Integer;
  MacAddr: array[0..5] of Byte;
  PhLen, i: Integer;
  SocketHint: TAddrInfoW;
  SocketResult: PAddrInfoW;
  //addr: in_addr;
begin
  Result := False;
  sMacAddress := '';
  WSAData.wVersion := 0;
  SocketResult := nil;
  //initialize
  WSAStartup(MakeWord(2, 0), WSAData);
  try
    //translate copmuter name to ip
    ZeroMemory(@SocketHint, sizeOf(TAddrInfoW));
    SocketHint.ai_socktype := SOCK_STREAM;
    SocketHint.ai_family := AF_UNSPEC;
    if GetAddrInfoW(PWideChar(sComputer), nil, @SocketHint, SocketResult) = 0 then
    begin
      //addr.S_un_b := SocketResult.ai_addr.sin_addr.S_un_b;
      //sIPAddress := String(inet_ntoa(addr));

      //translate ip address
      DestIP := SocketResult.ai_addr.sin_addr.S_addr;
      if DestIP <> INADDR_NONE then
      begin
        PhLen := SizeOf(MacAddr);
        ZeroMemory(@MacAddr[0], PhLen);
        //send arp packet and wait for response with mac address
        if SendARP(DestIP, 0, Pointer(@MacAddr[0]), @PhLen) = NO_ERROR then
        begin
          if (PhLen <> 0) then
          begin
            //format mac address
            for i := 0 to PhLen-1 do
              sMacAddress := sMacAddress + IntToHex(MacAddr[i], 2) + '-';
            SetLength(sMacAddress, Length(sMacAddress) - 1); //trunc last -

            Result := True;
          end;
        end;
      end;
    end;
  finally
    if Assigned(SocketResult) then FreeAddrInfoW(SocketResult);
    if WSAData.wVersion > 0 then WSACleanup;
  end;
end;

Open in new window


..NOTE: use plain symbolic name of computer like: myserver and for ip use something like:
10.20.1.33
0
 

Author Comment

by:plumothy
Comment Utility
sinisav: thanks for trying again.

In your code, GetAddrInfoW(PWideChar(sComputer), nil, @SocketHint, SocketResult) returns WSANO_DATA which is what I was getting before.
0
 
LVL 25

Accepted Solution

by:
Sinisa Vuk earned 250 total points
Comment Utility
Try it with:
...
SocketHint.ai_socktype := 0;
SocketHint.ai_family := 0;

Open in new window


did you try with more known computers?
-----
Try another tool as "second" opinion:
trogon-mac-scanner
0
 
LVL 19

Expert Comment

by:Thommy
Comment Utility
Maybe that's what you are looking for...
Get MAC Address of Remote or Local Machine
0
 
LVL 19

Assisted Solution

by:Thommy
Thommy earned 250 total points
Comment Utility
Have tried function GetMacAddress from my proposed link.

Got compile errors, because some if statements are missing the relational operators.

After inserting "<>" for the missing operators, it works fine for remote and local machines...


function GetMacAddress(const AServerName : string) : string;
 type
      TNetTransportEnum = function(pszServer : PWideChar;
                                   Level : DWORD;
                                   var pbBuffer : pointer;
                                   PrefMaxLen : LongInt;
                                   var EntriesRead : DWORD;
                                   var TotalEntries : DWORD;
                                   var ResumeHandle : DWORD) : DWORD; stdcall;

     TNetApiBufferFree = function(Buffer : pointer) : DWORD; stdcall;

     PTransportInfo = ^TTransportInfo;
      TTransportInfo = record
                        quality_of_service : DWORD;
                        number_of_vcs : DWORD;
                        transport_name : PWChar;
                        transport_address : PWChar;
                        wan_ish : boolean;
                      end;

var E,ResumeHandle,
     EntriesRead,
     TotalEntries : DWORD;
     FLibHandle : THandle;
     sMachineName,
     sMacAddr,
     Retvar : string;
     pBuffer : pointer;
     pInfo : PTransportInfo;
     FNetTransportEnum : TNetTransportEnum;
     FNetApiBufferFree : TNetApiBufferFree;
     pszServer : array[0..128] of WideChar;
     i,ii,iIdx : integer;
 begin
   sMachineName := trim(AServerName);
   Retvar := '00-00-00-00-00-00';

  // Add leading \\ if missing
   if (sMachineName <> '') and (length(sMachineName) = 2) then begin
     if copy(sMachineName,1,2) <> '\\' then
       sMachineName := '\\' + sMachineName
   end;

  // Setup and load from DLL
   pBuffer := nil;
   ResumeHandle := 0;
   FLibHandle := LoadLibrary('NETAPI32.DLL');

  // Execute the external function
   if FLibHandle <>0 then begin
     @FNetTransportEnum := GetProcAddress(FLibHandle,'NetWkstaTransportEnum');
     @FNetApiBufferFree := GetProcAddress(FLibHandle,'NetApiBufferFree');
     E := FNetTransportEnum(StringToWideChar(sMachineName,pszServer,129),0,
                            pBuffer,-1,EntriesRead,TotalEntries,Resumehandle);

    if E = 0 then begin
       pInfo := pBuffer;

      // Enumerate all protocols - look for TCPIP
       for i := 1 to EntriesRead do begin
         if pos('TCPIP',UpperCase(pInfo^.transport_name)) <>0 then begin
           // Got It - now format result 'xx-xx-xx-xx-xx-xx'
           iIdx := 1;
           sMacAddr := pInfo^.transport_address;

          for ii := 1 to 12 do begin
             Retvar[iIdx] := sMacAddr[ii];
             inc(iIdx);
             if iIdx in [3,6,9,12,15] then inc(iIdx);
           end;
         end;

        inc(pInfo);
       end;
       if pBuffer  <>nil then FNetApiBufferFree(pBuffer);
     end;

    try
       FreeLibrary(FLibHandle);
     except
       // Silent Error
     end;
   end;

  Result := Retvar;
 end;

Open in new window

0
 

Author Closing Comment

by:plumothy
Comment Utility
Thank you - it is working now. Points shared between sinsav and Thommy.

As suggested, I tried the "2nd opinion" tool (Trogon MAC Scanner). It failed to see any devices that were connected wirelessly and it displayed Host Name for less than one third of the computers that it found.

I was trying to find a wireless laptop. So, I connected another one with a cable. It was only then that your solutions started to work. They then found both the wired and wireless laptops (previously they were finding nothing).

I find that a bit weird and reckon that it is all rather unreliable!  Anyway, thanks for your help.
0

Featured Post

Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

Join & Write a Comment

Does the idea of dealing with bits scare or confuse you? Does it seem like a waste of time in an age where we all have terabytes of storage? If so, you're missing out on one of the core tools in every professional programmer's toolbox. Learn how to …
Whether you've completed a degree in computer sciences or you're a self-taught programmer, writing your first lines of code in the real world is always a challenge. Here are some of the most common pitfalls for new programmers.
An introduction to basic programming syntax in Java by creating a simple program. Viewers can follow the tutorial as they create their first class in Java. Definitions and explanations about each element are given to help prepare viewers for future …
Viewers will learn how to properly install Eclipse with the necessary JDK, and will take a look at an introductory Java program. Download Eclipse installation zip file: Extract files from zip file: Download and install JDK 8: Open Eclipse and …

771 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now