Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Getting the Physical (MAC) address of a Network Interface Card and finding out if it is the primary adapter on a multi-homed system

0.00/5 (No votes)
14 Mar 2006 4  
This article describes how to obtain a Network Interface Card's (NIC) Physical or Ethernet (MAC) address. It also shows how to identify if a network adapter is the primary adapter on a multi-homed system. If not, it shows how to find and get the primary adapter's IP and MAC addresses.

NetCfg - Version 2 Demo

Note: The actual IP and MAC addresses have been replaced by fictitious values for safety reasons.

Introduction

This article describes how to get the MAC or physical address of a network adapter, and how to tell if an adapter is the primary adapter on the system given its adapter (system) index. Finally, it shows how to get the (array) index of the primary adapter. It is based on an article by Joseph Dempsey: The "New ipconfig" and the IP Helper API.

Background

While "ipconfig /all" can be used to obtain detailed information about the IP configuration of a system, from the Windows command prompt, sometimes, we need this information in an application. There are many ways to do this, one of which may be to send the "ipconfig /all" command from your application and then parse the results for specific information you are looking for; however, this method gives you just the information and no real control over the network adapter.

The CNetworkAdapter class does both, it not only provides you with information but also lets you perform useful network configuration tasks such as renewing or releasing a lease on the network adapter. The "NetCfg -- The Sequel" project in Joseph's original article provided all of the above functionality. I needed to get the MAC address of a network adapter as well as find out if it was the primary adapter on a multi-homed system (a system with more than one Network Interface Card). Therefore, I decided to modify the CNetworkAdapter class by adding this new functionality. I also had to modify the CNetCfgDlg class to add functions to find if an adapter with a given index was the primary adapter, and to find out the index of the primary adapter and show its information.

Using the code

The groundwork for getting the MAC address of a network adapter was already laid out in Joseph's article. I had to modify the BOOL CNetworkAdapter::SetupAdapterInfo(IP_ADAPTER_INFO* pAdaptInfo ) function by first setting the adapter address length, and then copying each byte from pAdaptInfo->Address (the MAC address) to the adapter address structure as shown below:

BOOL CNetworkAdapter::SetupAdapterInfo(IP_ADAPTER_INFO* pAdaptInfo )

.....
m_ucAddress.nLen = pAdaptInfo->AddressLength;
for (int i = 0; i < (int) m_ucAddress.nLen; i++)
{
    m_ucAddress.ucAddress[i] = pAdaptInfo->Address[i];
}
.....

Getting the MAC Address

I wrote two overloaded functions to get the MAC address: one returns the address formatted as a standard MAC address in HEX, punctuated with hyphens, e.g., "00-12-34-AB-CD-EF".

// rsheikh: returns formatted MAC address in HEX punctuated with hyphens "-"

tstring CNetworkAdapter::GetAdapterAddress(void)
{
    tstring sAddr = _T("");
    CString sTemp = _T("");
    for (unsigned int i = 0; i < m_ucAddress.nLen; i++)
    {
        if(i > 0)
        {
            sAddr += "-";
        }

        sTemp.Format("%02X", m_ucAddress.ucAddress[i]);
        sAddr += sTemp;
    }

    return sAddr;
}

The other copies the raw MAC address bytes into a CByteArray passed into the function.

// rsheikh: copies the adapter's address raw bytes

// into the CByteArray pointed to by pAdapterAddress

void CNetworkAdapter::GetAdapterAddress(CByteArray& pAdapterAddress)
{
    for (unsigned int i = 0; i < m_ucAddress.nLen; i++)
    {
        pAdapterAddress.Add(m_ucAddress.ucAddress[i]);
    }
}

Finding out if the adapter with the given index is the primary adapter

In order to find out if the adapter with the given index is the primary adapter, I had to add a function to the dialog class CNetCfgDlg. This code iterates over the m_pAdapters array, comparing the given adapter index with the index for each adapter in the array. If the given adapter index is equal to the smallest index of all adapters in the array, then it is the primary adapter:

// Rafique Sheikh: This function can be used

// to find out if an Adapter with the given Index

// is the primary adapter.

bool CNetCfgDlg::IsPrimaryAdapter(DWORD dwIndex)
{
    bool bIsPrimaryAdapter = false;
    if( m_pAdapters ) 
    {    
        CNetworkAdapter* pAdapt = NULL;

        DWORD dwMinIndex = m_pAdapters->GetAdapterIndex() ;
        for (unsigned int i = 0; i < m_nCount; i++)
        {
            pAdapt = &m_pAdapters[ i ];
            if(pAdapt->GetAdapterIndex() < dwMinIndex)
            {
                dwMinIndex = pAdapt->GetAdapterIndex();
            }

        }

        if (dwIndex == dwMinIndex)
            bIsPrimaryAdapter = true;
    }

    return bIsPrimaryAdapter;
}

If the currently displayed/selected adapter is the primary adapter, the application GUI indicates that with a "YES", if not the GUI shows a "NO" and also makes the information for the primary adapter visible.

Getting the index of the primary adapter

In order to get information for the primary adapter, I had to add another function to CNetCfgDlg:

// Rafique Sheikh: This function returns the actual

// zero-based index of the Primary Adapter within the

// array of Adpaters pointed to by m_pAdapters 

int CNetCfgDlg::GetPrimaryAdapterIndex(void)
{
    int nPrimaryAdapterIndex = -1;
    if( m_pAdapters ) 
    {    
        CNetworkAdapter* pAdapt = NULL;
        DWORD dwMinIndex = m_pAdapters->GetAdapterIndex() ;
        nPrimaryAdapterIndex = 0;
        for (unsigned int i = 0; i < m_nCount; i++)
        {
            pAdapt = &m_pAdapters[ i ];
            if(pAdapt->GetAdapterIndex() < dwMinIndex)
            {
                dwMinIndex = pAdapt->GetAdapterIndex();
                nPrimaryAdapterIndex = i;
            }
        }
    }

    return nPrimaryAdapterIndex;
}

Once we have the index of the primary adapter, we can find all information about it from the array m_pAdapters.

Points of Interest

Getting the MAC address of a network adapter using the CNetworkAdapter class was simple. Finding which adapter is the primary on a computer system required some research on the topic. I could not find an authoritative answer to this question. It made sense that the first adapter listed in the response to the ipconfig /all command would be the primary, but I had no way to confirm this. The closest thing I found as an answer was an article by CISCO.

So it appeared that my assumption above was correct. I consistently found that the first adapter listed in response to ipconfig /all had the lowest index in the array m_pAdapters. The functions listed above to find out if the adapter with the given index was the primary and to find the index of the primary adapter are based on this assumption.

For instance, on a system with two network adapters, NIC1 and NIC2, let's say the adapter index for NIC1 is 65540 whereas the index for NIC2 is 65539, thus, based on the assumption I just listed, NIC2 is the primary adapter. When I talk about the index of the primary adapter, I do not mean the adapter's index on the computer system (65539 or 65540, in this case) but rather in the array m_pAdapters (0 or 1 in this case, since we only have two adapters). In this example, the index for NIC2 is 1 even though it is the primary adapter. It sounds strange but that is how the adapters get added to the array m_pAdapters, and that is why we need the functions listed above to find all this information.

If someone has a definite answer to "how to find the primary adapter on a multi-homed system?", I would love to hear about it.

That's it! Please go ahead and experiment, suggest other ways to accomplish the same. Enjoy!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here