1. 程式人生 > >獲取MAC地址方法大全

獲取MAC地址方法大全

Windows平臺下用C++程式碼取得機器的MAC地址並不是一件簡單直接的事情。到目前為止,作者尚未發現有任何一個通用的100%的適用於所有Windows平臺的方法可以穩定的取得MAC地址。而有些應用(比如MMORPG)則需要穩定的得到機器的MAC地址,解決方案往往是通過多種方法依次使用來提高成功率。

            說明:
  •  
    • 以下方法只會返回多網絡卡的第一個MAC地址。
    • 網上有很多文章和原始碼來解決該問題,大多不全或有問題。本篇所有方法均經過整理除錯,可直接使用。
    • 作者也不喜歡滿篇帖程式碼,本篇貼程式碼是方便直接使用,請讀者諒解。
    下面奉上幾種常用的方法:     [
Netbios
 is not supported on Windows Vista, Windows Server 2008, and subsequent versions of the operating system]
#include <windows.h> #pragma comment(lib, "Netapi32.lib") namespace { bool GetAdapterInfo(int adapterNum, std::string& macOUT) { NCB Ncb; memset(&Ncb, 0, sizeof(Ncb)); Ncb.ncb_command = NCBRESET; // 重置網絡卡,以便我們可以查詢 Ncb.ncb_lana_num = adapterNum; if (
Netbios
(&Ncb) != NRC_GOODRET) return false; // 準備取得介面卡的狀態塊 memset(&Ncb, sizeof(Ncb), 0); Ncb.ncb_command = NCBASTAT; Ncb.ncb_lana_num = adapterNum; strcpy((char *) Ncb.ncb_callname, "*"); struct ASTAT { ADAPTER_STATUS adapt; NAME_BUFFER nameBuff[30]; }adapter; memset(&adapter,sizeof(adapter), 0); Ncb.ncb_buffer = (unsigned char *)&adapter; Ncb.ncb_length = sizeof(adapter); if (
Netbios
(&Ncb) != 0) return false; char acMAC[32]; sprintf(acMAC, "%02X-%02X-%02X-%02X-%02X-%02X", int (adapter.adapt.adapter_address[0]), int (adapter.adapt.adapter_address[1]), int (adapter.adapt.adapter_address[2]), int (adapter.adapt.adapter_address[3]), int (adapter.adapt.adapter_address[4]), int (adapter.adapt.adapter_address[5])); macOUT = acMAC; return true; } } bool GetMacByNetBIOS(std::string& macOUT) { // 取得網絡卡列表 LANA_ENUM adapterList; NCB Ncb; memset(&Ncb, 0, sizeof(NCB)); Ncb.ncb_command = NCBENUM; Ncb.ncb_buffer = (unsigned char *)&adapterList; Ncb.ncb_length = sizeof(adapterList); // 取得MAC for (int i = 0; i < adapterList.length; ++i) { if (GetAdapterInfo(adapterList.lana[i], macOUT)) return true; } return false; }
參考:
方法二:通過對控制檯ipconfig /all命令重定向
#include <Windows.h> #include <boost/regex.hpp> namespace { #if 0 /// @brief 採用字串查詢來提取MAC地址 /// @remark 該方法有很大侷限性,並不是所有OS返回的MAC地址前導字串都是 ///     "Physical Address. . . . . . . . . : " bool ParseMac(const std::string& str, std::string& macOUT) { static const std::string beginMarkOfMAC("Physical Address. . . . . . . . . : "); static const std::string endMarkOfMAC("/r/n"); size_t begin = str.find(beginMarkOfMAC); if(begin != std::string::npos) { begin += beginMarkOfMAC.size(); size_t end = str.find(endMarkOfMAC, begin); if(end != std::string::npos) { macOUT = str.substr(begin, end - begin - 1); return true; } } return false; } #else bool ParseMac(const std::string& str, std::string& macOUT) { const static boost::regex expression( "([0-9a-fA-F]{2})-([0-9a-fA-F]{2})-([0-9a-fA-F]{2})-([0-9a-fA-F]{2})-([0-9a-fA-F]{2})-([0-9a-fA-F]{2})",  boost::regex::perl | boost::regex::icase); boost::cmatch what;  if(boost::regex_search(str.c_str(), what, expression))  { macOUT = what[1] + "-" + what[2] + "-" + what[3] + "-" + what[4] + "-" + what[5] + "-" + what[6]; return true; } return false; } #endif } bool GetMacByCmd(std::string& macOUT) { bool ret = false; //初始化返回MAC地址緩衝區 SECURITY_ATTRIBUTES sa;  sa.nLength = sizeof(SECURITY_ATTRIBUTES);  sa.lpSecurityDescriptor = NULL;  sa.bInheritHandle = TRUE;  //建立管道 HANDLE hReadPipe,hWritePipe; if(CreatePipe(&hReadPipe, &hWritePipe, &sa, 0) == TRUE) { //控制命令列視窗資訊 STARTUPINFO si;  //返回程序資訊 PROCESS_INFORMATION pi; si.cb = sizeof(STARTUPINFO);  GetStartupInfo(&si);  si.hStdError = hWritePipe;  si.hStdOutput = hWritePipe;  si.wShowWindow = SW_HIDE; //隱藏命令列視窗 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; //建立獲取命令列程序 if (CreateProcess(NULL, "ipconfig /all", NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi) == TRUE)  {  WaitForSingleObject(pi.hProcess, 3000); // 設定超時時間,防止Vista、Win7等作業系統卡死 unsigned long count; CloseHandle(hWritePipe); std::string strBuffer(1024 * 10, '/0'); // 準備足夠大的緩衝區 if(ReadFile(hReadPipe, const_cast<char*>(strBuffer.data()), strBuffer.size() - 1, &count, 0) == TRUE) { strBuffer.resize(strBuffer.find_first_of('/0')); // 截掉緩衝區後面多餘的'/0' ret = ParseMac(strBuffer, macOUT);//提取MAC地址串 } CloseHandle(pi.hThread);  CloseHandle(pi.hProcess);  } CloseHandle(hWritePipe); // VS2010下除錯,此處會有“An invalid handle was specified”的中斷,直接執行正常,原因未知。VS2008上正常。 CloseHandle(hReadPipe); } return ret; }
參考: 方法三:通過SNMP(簡單網路訪問協議)
#include <snmp.h> #pragma comment(lib, "snmpapi.lib") #pragma comment(lib, "Ws2_32.lib") bool GetMacBySNMP(std::string& macOUT) { bool ret = false; WSADATA WinsockData; if (WSAStartup(MAKEWORD(2, 0), &WinsockData) != 0)  return false; // Load the SNMP dll and get the addresses of the functions necessary const HINSTANCE m_dll = LoadLibrary("inetmib1.dll"); if (m_dll < (HINSTANCE) HINSTANCE_ERROR) return false; const PFNSNMPEXTENSIONINIT f_SnmpExtensionInit = (PFNSNMPEXTENSIONINIT) GetProcAddress(m_dll, "SnmpExtensionInit"); const PFNSNMPEXTENSIONINITEX f_SnmpExtensionInitEx = (PFNSNMPEXTENSIONINITEX) GetProcAddress(m_dll, "SnmpExtensionInitEx"); const PFNSNMPEXTENSIONQUERY f_SnmpExtensionQuery = (PFNSNMPEXTENSIONQUERY) GetProcAddress(m_dll, "SnmpExtensionQuery"); const PFNSNMPEXTENSIONTRAP f_SnmpExtensionTrap = (PFNSNMPEXTENSIONTRAP) GetProcAddress(m_dll, "SnmpExtensionTrap"); HANDLE pollForTrapEvent; AsnObjectIdentifier supportedView; f_SnmpExtensionInit(GetTickCount(), &pollForTrapEvent, &supportedView); // Initialize the variable list to be retrieved by f_SnmpExtensionQuery const AsnObjectIdentifier MIB_NULL = { 0, 0 }; RFC1157VarBind varBind[2]; varBind[0].name = MIB_NULL; varBind[1].name = MIB_NULL; RFC1157VarBindList varBindList; varBindList.list = varBind; UINT OID_ifEntryType[] = { 1, 3, 6, 1, 2, 1, 2, 2, 1, 3 }; UINT OID_ifEntryNum[] = { 1, 3, 6, 1, 2, 1, 2, 1 }; UINT OID_ipMACEntAddr[] = { 1, 3, 6, 1, 2, 1, 2, 2, 1, 6 }; AsnObjectIdentifier MIB_ifMACEntAddr = { sizeof(OID_ipMACEntAddr) / sizeof(UINT), OID_ipMACEntAddr }; AsnObjectIdentifier MIB_ifEntryType = { sizeof(OID_ifEntryType) / sizeof(UINT), OID_ifEntryType }; AsnObjectIdentifier MIB_ifEntryNum = { sizeof(OID_ifEntryNum) / sizeof(UINT), OID_ifEntryNum }; // Copy in the OID to find the number of entries in the Inteface table varBindList.len = 1;        // Only retrieving one item SnmpUtilOidCpy(&varBind[0].name, &MIB_ifEntryNum); AsnInteger errorStatus; AsnInteger errorIndex; f_SnmpExtensionQuery(ASN_RFC1157_GETNEXTREQUEST, &varBindList, &errorStatus, &errorIndex); varBindList.len = 2; // Copy in the OID of ifType, the type of interface SnmpUtilOidCpy(&varBind[0].name, &MIB_ifEntryType); // Copy in the OID of ifPhysAddress, the address SnmpUtilOidCpy(&varBind[1].name, &MIB_ifMACEntAddr); for(int j = 0; j < varBind[0].value.asnValue.number; j++) { // Submit the query.  Responses will be loaded into varBindList. // We can expect this call to succeed a # of times corresponding to the # of adapters reported to be in the system if(f_SnmpExtensionQuery(ASN_RFC1157_GETNEXTREQUEST, &varBindList, &errorStatus, &errorIndex) == FALSE) continue; // Confirm that the proper type has been returned if(SnmpUtilOidNCmp(&varBind[0].name, &MIB_ifEntryType, MIB_ifEntryType.idLength) != 0) continue; // Type 6 describes ethernet interfaces if(varBind[0].value.asnValue.number != 6)  continue; // Confirm that we have an address here if(SnmpUtilOidNCmp(&varBind[1].name, &MIB_ifMACEntAddr, MIB_ifMACEntAddr.idLength) != 0) continue; if(varBind[1].value.asnValue.address.stream == NULL) continue; // Ignore all dial-up networking adapters if ((varBind[1].value.asnValue.address.stream[0] == 0x44) && (varBind[1].value.asnValue.address.stream[1] == 0x45) && (varBind[1].value.asnValue.address.stream[2] == 0x53) && (varBind[1].value.asnValue.address.stream[3] == 0x54) && (varBind[1].value.asnValue.address.stream[4] == 0x00))  continue; // Ignore NULL addresses returned by other network interfaces if ((varBind[1].value.asnValue.address.stream[0] == 0x00) && (varBind[1].value.asnValue.address.stream[1] == 0x00) && (varBind[1].value.asnValue.address.stream[2] == 0x00) && (varBind[1].value.asnValue.address.stream[3] == 0x00) && (varBind[1].value.asnValue.address.stream[4] == 0x00) && (varBind[1].value.asnValue.address.stream[5] == 0x00))  continue; char buf[32]; sprintf(buf, "%02X-%02X-%02X-%02X-%02X-%02X", varBind[1].value.asnValue.address.stream[0], varBind[1].value.asnValue.address.stream[1], varBind[1].value.asnValue.address.stream[2], varBind[1].value.asnValue.address.stream[3], varBind[1].value.asnValue.address.stream[4], varBind[1].value.asnValue.address.stream[5]); macOUT = buf; ret = true; break; } // Free the bindings SnmpUtilVarBindFree(&varBind[0]); SnmpUtilVarBindFree(&varBind[1]); return ret; }
方法四:通過GetAdaptersInfo函式(適用於Windows 2000及以上版本)
#include <winsock2.h> #include <iphlpapi.h> #pragma comment(lib, "IPHLPAPI.lib") bool GetMacByGetAdaptersInfo(std::string& macOUT) { bool ret = false; ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO); PIP_ADAPTER_INFO pAdapterInfo = (IP_ADAPTER_INFO*)malloc(sizeof(IP_ADAPTER_INFO)); if(pAdapterInfo == NULL) return false; // Make an initial call to GetAdaptersInfo to get the necessary size into the ulOutBufLen variable if(GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW)  { free(pAdapterInfo); pAdapterInfo = (IP_ADAPTER_INFO *)malloc(ulOutBufLen); if (pAdapterInfo == NULL)  return false; } if(GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == NO_ERROR) { for(PIP_ADAPTER_INFO pAdapter = pAdapterInfo; pAdapter != NULL; pAdapter = pAdapter->Next) { // 確保是乙太網 if(pAdapter->Type != MIB_IF_TYPE_ETHERNET) continue; // 確保MAC地址的長度為 00-00-00-00-00-00 if(pAdapter->AddressLength != 6) continue; char acMAC[32]; sprintf(acMAC, "%02X-%02X-%02X-%02X-%02X-%02X", int (pAdapter->Address[0]), int (pAdapter->Address[1]), int (pAdapter->Address[2]), int (pAdapter->Address[3]), int (pAdapter->Address[4]), int (pAdapter->Address[5])); macOUT = acMAC; ret = true; break; } } free(pAdapterInfo); return ret; }
參考:
#include <winsock2.h> #include <iphlpapi.h> #pragma comment(lib, "IPHLPAPI.lib") bool GetMacByGetAdaptersAddresses(std::string& macOUT) { bool ret = false; ULONG outBufLen = sizeof(IP_ADAPTER_ADDRESSES); PIP_ADAPTER_ADDRESSES pAddresses = (IP_ADAPTER_ADDRESSES*)malloc(outBufLen); if (pAddresses == NULL)  return false; // Make an initial call to GetAdaptersAddresses to get the necessary size into the ulOutBufLen variable if(GetAdaptersAddresses(AF_UNSPEC, 0, NULL, pAddresses, &outBufLen) == ERROR_BUFFER_OVERFLOW) { free(pAddresses); pAddresses = (IP_ADAPTER_ADDRESSES*)malloc(outBufLen); if (pAddresses == NULL)  return false; } if(GetAdaptersAddresses(AF_UNSPEC, 0, NULL, pAddresses, &outBufLen) == NO_ERROR) { // If successful, output some information from the data we received for(PIP_ADAPTER_ADDRESSES pCurrAddresses = pAddresses; pCurrAddresses != NULL; pCurrAddresses = pCurrAddresses->Next) { // 確保MAC地址的長度為 00-00-00-00-00-00 if(pCurrAddresses->PhysicalAddressLength != 6) continue; char acMAC[32]; sprintf(acMAC, "%02X-%02X-%02X-%02X-%02X-%02X", int (pCurrAddresses->PhysicalAddress[0]), int (pCurrAddresses->PhysicalAddress[1]), int (pCurrAddresses->PhysicalAddress[2]), int (pCurrAddresses->PhysicalAddress[3]), int (pCurrAddresses->PhysicalAddress[4]), int (pCurrAddresses->PhysicalAddress[5])); macOUT = acMAC; ret = true; break; } }  free(pAddresses); return ret; }