Finding+the+MAC+address

//by Richard Russell, May 2007//

This article lists no fewer than **five** different methods of discovering the MAC address (Media Access Control address or physical address) of one or more network adaptors in the host system. The MAC address can be useful, amongst other things, as a unique identifier of the host PC. Each method has its own strengths and weaknesses.

Method 1
This method is the simplest, and works on all versions of Windows from Windows 95 to Windows Vista or later. However if two or more network adaptors are fitted it returns only the MAC address of the primary adaptor. Also, it relies on a feature of Windows which isn't guaranteed to be reliable, so should be used at your own risk!

code format="bb4w" SYS "LoadLibrary", "RPCRT4.DLL" TO rpcrt4% SYS "GetProcAddress", rpcrt4%, "UuidCreate" TO UuidCreate% SYS "GetProcAddress", rpcrt4%, "UuidCreateSequential" TO UuidCreateSequential% IF UuidCreateSequential% THEN UuidCreate% = UuidCreateSequential% DIM uuid{skip&(9), macaddress&(5)} SYS UuidCreate%, uuid{} MACaddress$ = RIGHT$("0"+STR$~uuid.macaddress&(0), 2) + "-" + \ \            RIGHT$("0"+STR$~uuid.macaddress&(1), 2) + "-" + \ \            RIGHT$("0"+STR$~uuid.macaddress&(2), 2) + "-" + \ \            RIGHT$("0"+STR$~uuid.macaddress&(3), 2) + "-" + \ \            RIGHT$("0"+STR$~uuid.macaddress&(4), 2) + "-" + \ \            RIGHT$("0"+STR$~uuid.macaddress&(5), 2) PRINT "MAC address = " MACaddress$ code

Method 2
This method is almost as simple as the preceding one, but works **only under Windows 2000 Professional, Windows XP or Windows Vista or later**. It also returns the MAC address of the primary adaptor, but uses documented functions which should be trustworthy:

code format="bb4w" INSTALL @lib$+"SOCKLIB" PROC_initsockets SYS "LoadLibrary", "IPHLPAPI.DLL" TO iphlpapi% SYS "GetProcAddress", iphlpapi%, "SendARP" TO `SendARP` hostent% = FN_sethost(FN_gethostname) hostip% = !!hostent%!12 DIM macaddress&(7) size% = 8 SYS `SendARP`, hostip%, 0, ^macaddress&(0), ^size% MACaddress$ = RIGHT$("0"+STR$~macaddress&(0), 2) + "-" + \ \            RIGHT$("0"+STR$~macaddress&(1), 2) + "-" + \ \            RIGHT$("0"+STR$~macaddress&(2), 2) + "-" + \ \            RIGHT$("0"+STR$~macaddress&(3), 2) + "-" + \ \            RIGHT$("0"+STR$~macaddress&(4), 2) + "-" + \ \            RIGHT$("0"+STR$~macaddress&(5), 2) PRINT "MAC address = " MACaddress$ code

Method 3
This method works on versions of Windows from Windows 95 to Windows XP, but //only if NetBIOS is installed//. It is also documented as being "unreliable" on Windows 95, 98 and Me. Therefore it is generally not a good choice:

code format="bb4w" NCBNAMSZ = 16 NCBRESET = &32 NCBASTAT = &33 SYS "LoadLibrary", "NETAPI32.DLL" TO netapi32% SYS "GetProcAddress", netapi32%, "Netbios" TO `Netbios` DIM ncb{command&, retcode&, lsn&, num&, buffer%, length{l&,h&}, \ \      callname&(NCBNAMSZ-1), name&(NCBNAMSZ-1), rto&, sto&, \ \      post%, lana_num&, cmd_cplt&, reserve&(9), event%} DIM ast{status&(59), namebuff&(18*30-1)} ncb.command& = NCBRESET SYS `Netbios`, ncb{} ncb.command& = NCBASTAT ncb.buffer% = ast{} ncb.length.l& = DIM(ast{}) ncb.callname& = "*              " ncb.lana_num& = 0 SYS `Netbios`, ncb{} MACaddress$ = RIGHT$("0"+STR$~ast.status&(0), 2) + "-" + \ \            RIGHT$("0"+STR$~ast.status&(1), 2) + "-" + \ \            RIGHT$("0"+STR$~ast.status&(2), 2) + "-" + \ \            RIGHT$("0"+STR$~ast.status&(3), 2) + "-" + \ \            RIGHT$("0"+STR$~ast.status&(4), 2) + "-" + \ \            RIGHT$("0"+STR$~ast.status&(5), 2) PRINT "MAC address = " MACaddress$ code

Method 4
This method can return the MAC address of multiple network adaptors, so is useful if you need information other than just for the primary adaptor. It works on versions of Windows from 98 to Vista or later (with acknowledgements to Jon Ripley on whose code this is based):

code format="bb4w" SYS "LoadLibrary", "iphlpapi.dll" TO iphlpapi% SYS "GetProcAddress", iphlpapi%, "GetAdaptersInfo" TO GetAdaptersInfo% DIM _IP_ADDR_STRING{AdrNext%, IpAddress&(15), IpMask&(15), NTEcontext%} DIM _IP_ADAPTER_INFO{Next%, ComboIndex%, AdapterName&(259), Description&(131), \ \                   MACadrLength%, MACaddress&(7), AdapterIndex%, \ \                   AdapterType%, DhcpEnabled%, CurrentIpAddress%, \ \                   IpAddressList{} = _IP_ADDR_STRING{}, \ \                   GatewayList{} = _IP_ADDR_STRING{}, \ \                   DhcpServer{} = _IP_ADDR_STRING{}, \ \                   HaveWins%, \ \                   PrimaryWinsServer{} = _IP_ADDR_STRING{}, \ \                   SecondaryWinsServer{} = _IP_ADDR_STRING{}, \ \                   LeaseObtained%, LeaseExpires%} SYS GetAdaptersInfo%, _IP_ADAPTER_INFO{}, ^OutBufLen% NumAdapters% = OutBufLen% DIV DIM(_IP_ADAPTER_INFO{}) DIM AdapterInfo{(NumAdapters%-1)} = _IP_ADAPTER_INFO{} SYS GetAdaptersInfo%, AdapterInfo{(0)}, ^OutBufLen% FOR i% = 0 TO NumAdapters% - 1 MACaddress$ = RIGHT$("0"+STR$~AdapterInfo{(i%)}.MACaddress&(0), 2) + "-" + \ \            RIGHT$("0"+STR$~AdapterInfo{(i%)}.MACaddress&(1), 2) + "-" + \ \            RIGHT$("0"+STR$~AdapterInfo{(i%)}.MACaddress&(2), 2) + "-" + \ \            RIGHT$("0"+STR$~AdapterInfo{(i%)}.MACaddress&(3), 2) + "-" + \ \            RIGHT$("0"+STR$~AdapterInfo{(i%)}.MACaddress&(4), 2) + "-" + \ \            RIGHT$("0"+STR$~AdapterInfo{(i%)}.MACaddress&(5), 2) PRINT "MAC address = " MACaddress$ NEXT i% code

Method 5
This method uses SNMP (the **Simple Network Management Protocol**). It too can return the MAC address of more than one adaptor, and runs on Windows 2000, Windows XP and Windows Vista or later. It uses the most complex code of any of the methods:

code format="bb4w" SYS "LoadLibrary", "INETMIB1.DLL" TO inetmib1% SYS "GetProcAddress", inetmib1%, "SnmpExtensionInit" TO `SnmpExtensionInit` SYS "GetProcAddress", inetmib1%, "SnmpExtensionQuery" TO `SnmpExtensionQuery` _ASN_RFC1157_GETNEXTREQUEST = &A1 DIM OID_ifEntryNum%(7) OID_ifEntryNum% = 1, 3, 6, 1, 2, 1, 2, 1 SYS "GlobalAlloc", 0, 32 TO MIB_ifEntryNum% FOR I% = 0 TO 7 MIB_ifEntryNum%!(4*I%) = OID_ifEntryNum%(I%) NEXT DIM OID_ifEntryType%(9) OID_ifEntryType% = 1, 3, 6, 1, 2, 1, 2, 2, 1, 3 SYS "GlobalAlloc", 0, 40 TO MIB_ifEntryType% FOR I% = 0 TO 9 MIB_ifEntryType%!(4*I%) = OID_ifEntryType%(I%) NEXT DIM OID_ipMACEntAddr%(9) OID_ipMACEntAddr% = 1, 3, 6, 1, 2, 1, 2, 2, 1, 6 SYS "GlobalAlloc", 0, 40 TO MIB_ipMACEntAddr% FOR I% = 0 TO 9 MIB_ipMACEntAddr%!(4*I%) = OID_ipMACEntAddr%(I%) NEXT DIM AsnObjectName{idLength%, pids%} DIM AsnObjectSyntax{asnType%, asnValue%, padding%(1)} DIM SupportedView{} = AsnObjectName{} DIM varBind{name0{}=AsnObjectName{}, value0{}=AsnObjectSyntax{}, \ \          name1{}=AsnObjectName{}, value1{}=AsnObjectSyntax{}} DIM varBindList{list%, len%} SYS "GetTickCount" TO tick% SYS `SnmpExtensionInit`, tick%, ^hPollForTrapEvent%, SupportedView{} varBind.name0.idLength% = DIM(OID_ifEntryNum%,1) + 1 varBind.name0.pids% = MIB_ifEntryNum% varBindList.len% = 1 varBindList.list% = varBind{} SYS `SnmpExtensionQuery`, _ASN_RFC1157_GETNEXTREQUEST, varBindList{}, \ \                        ^ErrorStatus%, ^ErrorIndex% varBindList.len% = 2 varBind.name0.idLength% = DIM(OID_ifEntryType%,1) + 1 varBind.name0.pids% = MIB_ifEntryType% varBind.name1.idLength% = DIM(OID_ipMACEntAddr%,1) + 1 varBind.name1.pids% = MIB_ipMACEntAddr% REPEAT SYS `SnmpExtensionQuery`, _ASN_RFC1157_GETNEXTREQUEST, varBindList{}, \ \                        ^ErrorStatus%, ^ErrorIndex% TO ret% IF ret% IF varBind.name0.idLength% >= 10 THEN FOR I% = 0 TO 9 ret% AND= (varBind.name0.pids%!(I%*4) = OID_ifEntryType%(I%)) NEXT IF ret% IF varBind.name1.idLength% >= 10 THEN FOR I% = 0 TO 9 ret% AND= (varBind.name1.pids%!(I%*4) = OID_ipMACEntAddr%(I%)) NEXT IF ret% IF varBind.value0.asnValue% = 6 THEN MACaddress$ = RIGHT$("0"+STR$~varBind.value1.asnValue%?0, 2) + "-" + \ \            RIGHT$("0"+STR$~varBind.value1.asnValue%?1, 2) + "-" + \ \            RIGHT$("0"+STR$~varBind.value1.asnValue%?2, 2) + "-" + \ \            RIGHT$("0"+STR$~varBind.value1.asnValue%?3, 2) + "-" + \ \            RIGHT$("0"+STR$~varBind.value1.asnValue%?4, 2) + "-" + \ \            RIGHT$("0"+STR$~varBind.value1.asnValue%?5, 2) PRINT "MAC address = " MACaddress$ ENDIF ENDIF ENDIF UNTIL ret% = 0 SYS "GlobalFree", varBind.name0.pids% SYS "GlobalFree", varBind.name1.pids% code