Solved

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

Posted on 2014-01-08
15
5,574 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 5
  • 4
  • 3
  • +2
15 Comments
 
LVL 53

Expert Comment

by:Will Szymkowski
ID: 39766871
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
ID: 39767256
Thanks, but I already know that. It is a Delphi function that I am after.
0
 
LVL 38

Expert Comment

by:Geert Gruwez
ID: 39767348
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
Get 15 Days FREE Full-Featured Trial

Benefit from a mission critical IT monitoring with Monitis Premium or get it FREE for your entry level monitoring needs.
-Over 200,000 users
-More than 300,000 websites monitored
-Used in 197 countries
-Recommended by 98% of users

 

Author Comment

by:plumothy
ID: 39767370
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 27

Expert Comment

by:Sinisa Vuk
ID: 39767377
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 38

Expert Comment

by:Geert Gruwez
ID: 39767391
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
ID: 39767421
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
 
LVL 27

Expert Comment

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

Expert Comment

by:Geert Gruwez
ID: 39767457
edited, wowa, misread the q ... my excuses
0
 
LVL 27

Expert Comment

by:Sinisa Vuk
ID: 39767638
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
ID: 39767776
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 27

Accepted Solution

by:
Sinisa Vuk earned 250 total points
ID: 39767857
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
ID: 39768074
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
ID: 39768270
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
ID: 39768388
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

Creating Instructional Tutorials  

For Any Use & On Any Platform

Contextual Guidance at the moment of need helps your employees/users adopt software o& achieve even the most complex tasks instantly. Boost knowledge retention, software adoption & employee engagement with easy solution.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

In this post we will learn how to connect and configure Android Device (Smartphone etc.) with Android Studio. After that we will run a simple Hello World Program.
What do responsible coders do? They don't take detrimental shortcuts. They do take reasonable security precautions, create important automation, implement sufficient logging, fix things they break, and care about users.
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 …
Suggested Courses
Course of the Month7 days, 3 hours left to enroll

623 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