C++でMACアドレスを取得する

MACアドレスは、LANカードやルーターなどのネットワーク機器に付けられた番号で、原則的に全世界で重複しない番号が機器毎に割り当てられています。

物理アドレス (Physical Address) とかノードID (Node ID) などと呼ばれる事もあるようです。

 

C++のプログラムからMACアドレスを取得する方法について解説します。

C#の場合はこちら↓

C#でMACアドレスを取得する


ヘッダーのインクルードとライブラリのリンク

ネットワークやネットワークインターフェース関連のライブラリ IP Helper API を使用します。

ヘッダーファイル「iphlpapi.h」をインクルードして、

ライブラリファイル「iphlpapi.lib」をリンクしましょう。 

#include <iphlpapi.h>
#pragma comment(lib, "iphlpapi.lib")

GetAdaptersAddressesメソッド

MACアドレスを取得するにはGetAdaptersAddressesメソッドを使います。

MACアドレスの他にもIPアドレスやネットワークインターフェースの種類など様々な情報が取得できます。

 

BOOL GetMacAddressTest()
{
    PIP_ADAPTER_ADDRESSES pAddresses;
    PIP_ADAPTER_ADDRESSES pCurrent;
    ULONG                 ret;
    ULONG                 size;
    CString               mac;

    
    //必要なバッファサイズを取得
    ret = ::GetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &size);
    if (ERROR_BUFFER_OVERFLOW != ret) return FALSE;

    //必要なバッファを確保
    pAddresses = (PIP_ADAPTER_ADDRESSES)malloc(size);
    if (NULL == pAddresses) return FALSE;

    //ネットワークインターフェース情報を取得
    ret = ::GetAdaptersAddresses(AF_UNSPEC, 0, NULL, pAddresses, &size);
    if (ERROR_SUCCESS != ret) {
        free(pAddresses);
        return FALSE;
    }
    pCurrent = pAddresses;
    while (NULL != pCurrent) {
        //ネットワーク接続の状態を確認
        if (IfOperStatusUp == pCurrent->OperStatus) {
            //ネットワーク接続の種類を確認
            if (IF_TYPE_SOFTWARE_LOOPBACK != pCurrent->IfType) {
                //MACアドレスを取得
                if (0 < pCurrent->PhysicalAddressLength) {
                    mac.Empty();
                    for (ULONG i = 0; i < pCurrent->PhysicalAddressLength; ++ i) {
                        mac.AppendFormat(L"%02X", pCurrent->PhysicalAddress[i]);
                    }
                    TRACE(L"Physical address: %s\n", (LPCTSTR)mac);
                }
            }
        }
        pCurrent = pCurrent->Next;
    }
    free(pAddresses);
    return TRUE;
}

情報取得に必要なバッファサイズを取得 (11行目)

GetAdaptersAddressesメソッドの第4引数をNULLにすると必要なバッファサイズを取得する事が出来ます。(第5引数に必要サイズが返ります)

この時メソッドの戻り値はERROR_BUFFER_OVERFLOWになります。

メモリを確保して情報を取得 (15~23行目)

必要なバッファサイズが分かったらそのサイズ分メモリを確保します。(15行目)

再度GetAdaptersAddressesメソッドを呼び出して情報を取得します。

(今度は第4引数に確保したメモリのポインタを渡します)

うまく取得できれば戻り値はERROR_SUCCESSになります。

ネットワークインターフェースの数だけループ (24~41行目)

取得できるネットワークインターフェースは複数存在する場合があります。

複数存在する場合には IP_ADAPTER_ADDRESSES 構造体の Next に次の構造体のポインタが格納されています。

Next が NULL になるまでループすれば全ての情報を確認する事が出来ます。

ネットワーク接続の状態を確認 (27行目)

IP_ADAPTER_ADDRESSES 構造体の OperStatus を見るとネットワークインターフェースの現在の操作状態を確認する事が出来ます。

上記の例では、稼働しているインターフェースのみを対象にMACアドレスを収集しています。

 

状態の種類については IP_ADAPTER_ADDRESSES の OperStatus を参照

ネットワーク接続の種類を確認 (29行目)

IP_ADAPTER_ADDRESSES 構造体の IfType を見るとネットワークインターフェースの種類を確認する事が出来ます。

上記の例では、ループバックアダプタを除外しています。

 

状態の種類については IP_ADAPTER_ADDRESSES の IfType を参照

MACアドレスを取得する (31~37行目)

IP_ADAPTER_ADDRESSES 構造体の PhysicalAddress にMACアドレスが格納されています。

PhysicalAddress はバイト配列で、配列のサイズは PhysicalAddressLength に格納されています。

メモリを解放 (42行目)

最後は忘れずに確保したメモリを開放しましょう。