C ++:在Vista上获取网络适配器的MAC地址?

时间:2008-10-21 13:34:31

标签: c++ windows-vista mac-address

我们目前正在使用NetBios method,它在XP下运行正常。 Vista下的初步测试表明它也有效,但有一些警告 - 例如,NetBIOS必须存在,而且从我读过的内容来看,适配器的顺序必将发生变化。我们的替代方法 - SNMPExtensionQuery - 似乎在Vista下被打破了。

问题是:您是否知道在Vista计算机上获取本地MAC地址列表的可靠方法?向后兼容XP是一个优点(我宁愿使用一个单一的方法,而不是许多丑陋的#ifdef)。谢谢!

6 个答案:

答案 0 :(得分:19)

这将为您提供计算机上所有MAC地址的列表。它也适用于所有版本的Windows:

void getdMacAddresses(std::vector<std::string> &vMacAddresses;)
{
    vMacAddresses.clear();
    IP_ADAPTER_INFO AdapterInfo[32];       // Allocate information for up to 32 NICs
    DWORD dwBufLen = sizeof(AdapterInfo);  // Save memory size of buffer
    DWORD dwStatus = GetAdaptersInfo(      // Call GetAdapterInfo
    AdapterInfo,                 // [out] buffer to receive data
    &dwBufLen);                  // [in] size of receive data buffer

    //No network card? Other error?
    if(dwStatus != ERROR_SUCCESS)
        return;

    PIP_ADAPTER_INFO pAdapterInfo = AdapterInfo;
    char szBuffer[512];
    while(pAdapterInfo)
    {
        if(pAdapterInfo->Type == MIB_IF_TYPE_ETHERNET)
        {
            sprintf_s(szBuffer, sizeof(szBuffer), "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x"
                , pAdapterInfo->Address[0]
                , pAdapterInfo->Address[1]
                , pAdapterInfo->Address[2]
                , pAdapterInfo->Address[3]
                , pAdapterInfo->Address[4]
                , pAdapterInfo->Address[5]
                );
            vMacAddresses.push_back(szBuffer);
        }
        pAdapterInfo = pAdapterInfo->Next;

    }
}

答案 1 :(得分:2)

你能使用WMIService吗?我用它来获取Vista之前的机器的mac地址。

答案 2 :(得分:1)

已经回答过的旧问题,但这是更安全的代码-以防无法完全初始化WMI。

要获取有关系统信息的访问权限,下面是一个极简主义类,它试图保持安全性:

注意:ToStringconvertToUtf8convertFromUtf8留给读者练习。 :)

注意:我刚刚展示了WMI系统的 safe 初始化和拆卸,以及从WMI获取值和获取MAC地址的基本知识(OP中的问题)

来自来自工作代码,但是在我将其粘贴到此处时进行了修改。因此有可能遗漏了其他应包括在内的事情。糟糕!

class WmiAccessor
{
public:
    WmiAccessor()
        : _pWbemLocator(NULL)
        , _pWbemServices(NULL)
        , _com_initialized(false)
        , _com_need_uninitialize(false)
        , _svc_initialized(false)
        , _loc_initialized(false)
        , _all_initialized(false)
        , _errors("")
        , m_mutex()
    {
        HRESULT hr;
        hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
        switch (hr)
        {
        case S_OK:
            // The COM library was initialized successfully on this thread.
            _com_initialized = true;
            _com_need_uninitialize = true;
            break;
        case S_FALSE:
            // The COM library is already initialized on this thread.
            _com_initialized = true;
            _com_need_uninitialize = true;
            break;
        case RPC_E_CHANGED_MODE:
            // A previous call to CoInitializeEx specified the concurrency model
            // for this thread as multithread apartment (MTA).
            //  This could also indicate that a change from neutral-threaded apartment to
            //  single-threaded apartment has occurred.
            _com_initialized = true;
            _com_need_uninitialize = false;
            break;
        default:
            _com_initialized = false;
            _com_need_uninitialize = false;
            _errors += "Failed to initialize COM.\r\n";
            return;
        }

        hr = ::CoInitializeSecurity(NULL, -1, NULL, NULL,
            0 /*RPC_C_AUTHN_LEVEL_DEFAULT*/,
            3 /*RPC_C_IMP_LEVEL_IMPERSONATE*/,
            NULL, EOAC_NONE, NULL);
        // RPC_E_TOO_LATE == Security must be initialized before!
        // It cannot be changed once initialized. I don't care!
        if (FAILED(hr) && (hr != RPC_E_TOO_LATE))
        {
            _errors += "Failed to initialize COM Security.\r\n";
            if (_com_need_uninitialize)
            {
                ::CoUninitialize();
                _com_need_uninitialize = false;
            }
            return;
        }

        hr = _pWbemLocator.CoCreateInstance(CLSID_WbemLocator);
        if (FAILED(hr) || (_pWbemLocator == nullptr))
        {
            _errors += "Failed to initialize WBEM Locator.\r\n";
            return;
        }
        _loc_initialized = true;

        hr = _pWbemLocator->ConnectServer(
            CComBSTR(L"root\\cimv2"), NULL, NULL, 0, NULL, 0, NULL, &_pWbemServices);
        if (FAILED(hr) || (_pWbemServices == nullptr))
        {
            _errors += "Failed to connect WBEM Locator.\r\n";
            _pWbemLocator.Release();
            _loc_initialized = false;
            return;
        }
        else
        {
            _svc_initialized = true;

            // Set security Levels on the proxy
            hr = CoSetProxyBlanket(_pWbemServices,
                RPC_C_AUTHN_WINNT,           // RPC_C_AUTHN_xxx
                RPC_C_AUTHZ_NONE,            // RPC_C_AUTHZ_xxx
                NULL,                        // Server principal name
                RPC_C_AUTHN_LEVEL_CALL,      // RPC_C_AUTHN_LEVEL_xxx
                RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
                NULL,                        // client identity
                EOAC_NONE                    // proxy capabilities
            );
            if (FAILED(hr))
            {
                _errors += "Failed to set proxy blanket.\r\n";
                return;
            }
        }

        _all_initialized = true;
    }

    ~WmiAccessor()
    {
        std::unique_lock<std::mutex> slock(m_mutex);

        if (_svc_initialized)
        {
            if (_pWbemServices)
                _pWbemServices.Release();
            _svc_initialized = false;
        }
        if (_loc_initialized)
        {
            if (_pWbemLocator)
                _pWbemLocator.Release();
            _loc_initialized = false;
        }
        if (_com_initialized)
        {
            if (_com_need_uninitialize)
            {
                ::CoUninitialize();
            }
            _com_initialized = false;
            _com_need_uninitialize = false;
        }
        _all_initialized = false;
    }

    // public: must lock
    std::string get_and_clear_errors()
    {
        std::string result = "";
        std::unique_lock<std::mutex> slock(m_mutex);
        std::swap(result, _errors);
        return result;
    }

    // public: must lock
    std::string get_string(const std::string& name, const std::string& dflt /*= ""*/)
    {
        std::unique_lock<std::mutex> slock(m_mutex);
        return _all_initialized ? _string(name) : dflt;
    }

    // public: must lock
    uint32_t get_uint32(const std::string& name, uint32_t dflt /*= 0*/)
    {
        std::unique_lock<std::mutex> slock(m_mutex);
        return _all_initialized ? _uint32(name) : dflt;
    }


    // similarly for other public accessors of basic types.


private:
    CComPtr<IWbemLocator> _pWbemLocator;
    CComPtr<IWbemServices> _pWbemServices;
    volatile bool _com_initialized;
    volatile bool _com_need_uninitialize;
    volatile bool _svc_initialized;
    volatile bool _loc_initialized;
    volatile bool _all_initialized;
    std::string _errors;
    CComVariant _variant(const std::wstring& name);
    std::string _string(const std::string& name);
    uint32_t _uint32(const std::string& name);
    uint16_t _uint16(const std::string& name);
    uint8_t _uint8(const std::string& name);
    std::vector<std::string> _macAddresses(bool forceReCalculate = false);
    // to protect internal objects, public methods need to protect the internals.
    //
    mutable std::mutex m_mutex;
    std::vector<std::string> _macs; // unlikely to change, so save them once found.

    // internal: assumes inside a lock
    CComVariant _variant(const std::wstring& name)
    {
        if (!_all_initialized)
            return CComVariant();

        CComPtr<IEnumWbemClassObject> pEnum;
        CComBSTR cbsQuery = std::wstring(L"Select " + name + L" from Win32_OperatingSystem").c_str();
        HRESULT hr = _pWbemServices->ExecQuery(
            CComBSTR(L"WQL"), cbsQuery, WBEM_FLAG_FORWARD_ONLY, NULL, &pEnum);
        CComVariant cvtValue;
        if (FAILED(hr) || !pEnum)
        {
            std::wstring wquery(cbsQuery, SysStringLen(cbsQuery));
            _errors += "Failed to exec WMI query: '" + convertToUtf8(wquery) + "'\r\n";
            return cvtValue;
        }
        ULONG uObjectCount = 0;
        CComPtr<IWbemClassObject> pWmiObject;
        hr = pEnum->Next(WBEM_INFINITE, 1, &pWmiObject, &uObjectCount);
        if (FAILED(hr) || !pWmiObject)
        {
            _errors
                += "Failed to get WMI Next result for: '" + convertToUtf8(name) + "'\r\n";
            return cvtValue;
        }
        hr = pWmiObject->Get(name.c_str(), 0, &cvtValue, 0, 0);
        if (FAILED(hr))
        {
            _errors
                += "Failed to get WMI result value for: '" + convertToUtf8(name) + "'\r\n";
        }
        return cvtValue;
    }

    // internal: assumes inside a lock
    std::string _string(const std::string& name)
    {
        if (!_all_initialized)
            return "";

        CComVariant cvtValue = _variant(convertFromUtf8(name).c_str());
        std::wstring wValue(cvtValue.bstrVal, SysStringLen(cvtValue.bstrVal));
        std::string sValue = convertToUtf8(wValue);
        return sValue;
    }

    // internal: assumes inside a lock
    uint32_t _uint32(const std::string& name)
    {
        if (!_all_initialized)
            return 0;

        CComVariant cvtValue = _variant(convertFromUtf8(name).c_str());
        uint32_t uValue = static_cast<uint32_t>(cvtValue.lVal);
        return uValue;
    }

    // similarly for other internal access of basic types.

    // internal: assumes inside a lock
    std::vector<std::string> _macAddresses(bool forceReCalculate /*= false*/)
    {
        if (!_all_initialized)
        {
            return _macs; // it will still be empty at this point.
        }
        if (forceReCalculate)
        {
            _macs.clear();
        }
        if (_macs.empty())
        {
            // hr == 0x80041010 == WBEM_E_INVALID_CLASS
            // hr == 0x80041017 == WBEM_E_INVALID_QUERY
            // hr == 0x80041018 == WBEM_E_INVALID_QUERY_TYPE
            CComBSTR cbsQuery = std::wstring(L"Select * from Win32_NetworkAdapter").c_str();
            CComPtr<IEnumWbemClassObject> pEnum;
            HRESULT hr = _pWbemServices->ExecQuery(
                CComBSTR(L"WQL"), cbsQuery, WBEM_RETURN_IMMEDIATELY, NULL, &pEnum);
            if (FAILED(hr))
            {
                _errors += "error: MacAddresses: ExecQuery('"
                           + convertToUtf8((LPWSTR)cbsQuery) + "') returned "
                           + ToString(hr) + "\r\n";
            }
            if (SUCCEEDED(hr))
            {
                ULONG fetched;
                VARIANT var;
                IWbemClassObject* pclsObj = NULL;
                while (pEnum)
                {
                    hr = pEnum->Next(WBEM_INFINITE, 1, &pclsObj, &fetched);
                    if (0 == fetched)
                        break;

                    std::string theMac = "";
                    VariantInit(&var);
                    hr = pclsObj->Get(L"MACAddress", 0, &var, 0, 0);
                    if (SUCCEEDED(hr))
                    {
                        switch (var.vt)
                        {
                            case VT_NULL: break;
                            case VT_BSTR:
                                theMac = (var.bstrVal == NULL)
                                       ? ""
                                       : convertToUtf8(var.bstrVal);
                                break;
                            case VT_LPSTR:
                                theMac = (var.bstrVal == NULL)
                                       ? ""
                                       : (const char*)var.bstrVal;
                                break;
                            case VT_LPWSTR:
                                theMac = (var.bstrVal == NULL)
                                       ? ""
                                       : convertToUtf8((LPWSTR)var.bstrVal);
                                break;
                            // _could_ be array of BSTR, LPSTR, LPWSTR; unlikely, but ....
                            case VT_ARRAY | VT_BSTR:
                            case VT_ARRAY | VT_LPSTR:
                            case VT_ARRAY | VT_LPWSTR:
                                _errors += "warning: MacAddresses: unexpected array of addresses";
                                _errors += "\r\n";

                                // yet another exercise for the reader :)
                                break;
                            default:
                                _errors += "error: MacAddresses: unexpected VARIANT.vt =  "
                                       + ToString(var.vt) + "\r\n";
                                break;
                        }
                        // local loopback has an empty address?
                        if (!theMac.empty())
                        {
                            _macs.push_back(theMac);
                        }
                    }
                    VariantClear(&var);
                    pclsObj->Release();
                }
            }
        }
        return _macs;
    }

...

}

答案 3 :(得分:0)

GetAdaptersInfo()是官方方法,它枚举所有适配器,甚至是断开连接的适配器 请参阅此帖子,例如代码codeguru

答案 4 :(得分:0)

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <stdio.h>
#include <vector>
#include <Windows.h>
#include <Iphlpapi.h>
#include <Assert.h>
#include <string>
#pragma comment(lib, "iphlpapi.lib")


char* getdMacAddresses()
{

    IP_ADAPTER_INFO AdapterInfo[32];       // Allocate information for up to 32 NICs
    DWORD dwBufLen = sizeof(AdapterInfo);  // Save memory size of buffer
    DWORD dwStatus = GetAdaptersInfo(      // Call GetAdapterInfo
        AdapterInfo,                 // [out] buffer to receive data
        &dwBufLen);                  // [in] size of receive data buffer

    //Exit When Error 
    if (dwStatus != ERROR_SUCCESS)
        return "ERROR";

    PIP_ADAPTER_INFO pAdapterInfo = AdapterInfo;
    char szBuffer[512];
    while (pAdapterInfo)
    {
        if (pAdapterInfo->Type == MIB_IF_TYPE_ETHERNET)
        {

            sprintf_s(szBuffer, sizeof(szBuffer), "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x"
                , pAdapterInfo->Address[0]
                , pAdapterInfo->Address[1]
                , pAdapterInfo->Address[2]
                , pAdapterInfo->Address[3]
                , pAdapterInfo->Address[4]
                , pAdapterInfo->Address[5]
                );

            return szBuffer; 

        }


        pAdapterInfo = pAdapterInfo->Next;

    }

    return "ERROR";
}

答案 5 :(得分:-1)

你可以在XP和Vista上使用WMI,网上有很多例子。例如: Use Windows Management Instrumentation (WMI) to get a MAC Address