转:获取MAC地址方法大全

转自http://dearymz.blog.163.com/blog/static/205657420110424426943/

转自https://blog.csdn.net/han2814675/article/details/6223617?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

Windows平台下用C++代码取得机器的MAC地址并不是一件简单直接的事情。到目前为止,作者尚未发现有任何一个通用的100%的适用于所有Windows平台的方法可以稳定的取得MAC地址。而有些应用(比如MMORPG)则需要稳定的得到机器的MAC地址,解决方案往往是通过多种方法依次使用来提高成功率。

            说明:

  •  
    • 以下方法只会返回多网卡的第一个MAC地址。
    • 网上有很多文章和源码来解决该问题,大多不全或有问题。本篇所有方法均经过整理调试,可直接使用。
    • 作者也不喜欢满篇帖代码,本篇贴代码是方便直接使用,请读者谅解。

    下面奉上几种常用的方法:


方法一:通过NetBIOS

    [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);

Netbios(&Ncb);

// 取得MAC

for (int i = 0; i < adapterList.length; ++i)

{

if (GetAdapterInfo(adapterList.lana[i], macOUT))

return true;

}

return false;

}

参考:

取得系统中网卡MAC地址的三种方法


方法二:通过对控制台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

/// @brief 采用boost::regex来提取MAC

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;

}

参考:

Boost编译

VC获取MAC地址的4种方法
 

方法三:通过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;

}

参考:

SNMP Provider

SNMP Functions

Install and Enable SNMP Service in Windows XP, Vista and 2003

Visual C++通过snmp获取mac地址

方法四:通过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;

}

参考:

http://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=13421

方法五:通过GetAdaptersAddresses函数(适用于Windows XP及以上版本)

#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;

}

转自http://dearymz.blog.163.com/blog/static/205657420110424426943/

发布了18 篇原创文章 · 获赞 10 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/u012077233/article/details/104648349