zoukankan      html  css  js  c++  java
  • c++ windows 获取mac地址

    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;
    }
     
    参考:


    方法二:通过对控制台ipconfig /all命令重定向
    #include <Windows.h>
    #include <boost egex.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(" ");
    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, ''); // 准备足够大的缓冲区
    if(ReadFile(hReadPipe, const_cast<char*>(strBuffer.data()), strBuffer.size() - 1, &count, 0) == TRUE)
    {
    strBuffer.resize(strBuffer.find_first_of('')); // 截掉缓冲区后面多余的''
    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;
    }
     
    参考:
     

    方法五:通过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;
    }
  • 相关阅读:
    (转) dedecms中自定义数据模型
    (转)dedecms网页模板编写
    (转)dedecms入门
    (转)浅谈dedecms模板引擎工作原理及自定义标签
    (转)PHP数组的总结(很全面啊)
    (转)echo和print的区别
    (转)dedecms代码详解 很全面
    (转)php 函数名称前的@有什么作用
    (转)PHP正则表达式的快速学习方法
    GIS中mybatis_CMEU的配置方法
  • 原文地址:https://www.cnblogs.com/xumaojun/p/8618334.html
Copyright © 2011-2022 走看看