Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / MFC

Programatically Configure an Ethernet Network Adapter on a Windows Machine Using C/MFC

5.00/5 (7 votes)
2 Mar 2016CPOL2 min read 25.6K   584  
Programatically set a static IP, subnet mask and gateway to an ethernet network adapter on your local machine

Introduction

I'm working on a commercial software related to an industrial field that happens to require specific network settings.

For reliability, we aim our software to work only with an ethernet adapter on a harsh environment but we don't really control the kind of computer our customers are using.

Nowadays, computers come with a variety of connection devices besides an ethernet card, such as bluetooth or wi-fi adapters. To make things worse, some programs like WMWare create virtual adapters for their own use. We need to automatically find a physical ethernet card and enforce a given set of parameters for our program to work properly.

The industrial field runs at a very different pace than software development and change happens slowly. That makes it necessary to support older OS like Windows XP as they are seldom updated, since most of the times the computers are not even connected to the internet.

As the code changes system settings, administrator privileges are required.

Background

The code below is a compilation of the examples shown on MSDN for GetAdaptersAddresses, WlanOpenHandle and WlanEnumInterfaces plus several ideas taken from different blogs.

Using the Code

The code is self explanatory. All dependencies are explicitly included in the cpp file. Additionally, this project relies on MFC for simple string manipulation with CString and convenient data handling with CMap.

For a non-MFC project, it should be simple to get rid of MFC classes by using C or C++ standard string manipulation routines. It should be necessary to get rid of CRegKey class and replace it with standard APIs too.

The first step is to retrieve all network adapters with IPv4 compatibility using GetAdaptersAddresses, as shown in the MSDN.

C++
CMapStringToString mapAdapters;

// Get network adapters for IPv4 using GetAdaptersAddresses
ULONG nFlags = GAA_FLAG_INCLUDE_PREFIX;
ULONG nFamily = AF_INET;
PIP_ADAPTER_ADDRESSES pAddresses = NULL;
PIP_ADAPTER_ADDRESSES pCurrAddress = NULL;
ULONG nBufLen = 16324, nTries = 0, nMaxTries = 3;
DWORD dwResult;

do
{
    pAddresses = (IP_ADAPTER_ADDRESSES*) MALLOC(nBufLen);
    if (pAddresses == NULL)
    {
        _tprintf(_T("Memory allocation failed for IP_ADAPTER_ADDRESSES struct\n"));
        return SNA_ERROR_MALLOC;
    }

    dwResult = GetAdaptersAddresses(nFamily, nFlags, NULL, pAddresses, &nBufLen);
    if (dwResult == ERROR_BUFFER_OVERFLOW)
    {
        FREE(pAddresses);
        pAddresses = NULL;
    }
    else
        break;

    nTries++;
}
while (dwResult == ERROR_BUFFER_OVERFLOW && nTries < nMaxTries);

if (dwResult != NO_ERROR)
{
    _tprintf(_T("Call to GetAdaptersAddresses failed with error: %d\n"), dwResult);
    FREE(pAddresses);
    return SNA_ERROR_GETADAPTERSADDRESS;
}

pCurrAddress = pAddresses;
while (pCurrAddress)
{
    if (pCurrAddress->IfType == IF_TYPE_ETHERNET_CSMACD)
    {
        USES_CONVERSION;
        CString cKey = A2T(pCurrAddress->AdapterName);
        mapAdapters[cKey] = pCurrAddress-&FriendlyName;
    }

    pCurrAddress = pCurrAddress-&Next;
}
FREE(pAddresses);

if (mapAdapters.GetCount() == 0)
{
    _tprintf(_T("No ethernet adapter found\n"));
    return SNA_ERROR_NOADAPTERFOUND;
}

On older OSes like Windows XP, wireless network cards are reported as IF_TYPE_ETHERNET_CSMACD.

Gather identifiers of wireless adapters and remove them for the list generated in the step above.

C++
OSVERSIONINFO osvi;
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osvi);
if (osvi.dwMajorVersion < 6)
{
    _tprintf(_T("Compatibility mode\n"));

    HANDLE hClient = NULL;
    DWORD dwMaxClient = 2;
    DWORD dwCurVersion = 0;
    PWLAN_INTERFACE_INFO_LIST pIfList = NULL;
    PWLAN_INTERFACE_INFO pIfInfo = NULL;

    dwResult = WlanOpenHandle(dwMaxClient, NULL, &dwCurVersion, &hClient);
    if (dwResult == ERROR_SUCCESS)
    {
        dwResult = WlanEnumInterfaces(hClient, NULL, &pIfList);
        if (dwResult == ERROR_SUCCESS)
        {
            WCHAR szWlanGuid[256] = { 0 };
            int nMaxLen = 256;

            for (int i = 0; i < (int) pIfList->dwNumberOfItems; i++)
            {
                pIfInfo = (WLAN_INTERFACE_INFO *) &pIfList->InterfaceInfo[i];
                if (StringFromGUID2(pIfInfo->InterfaceGuid,
                    (LPOLESTR) szWlanGuid, nMaxLen) > 0)
                {
                    CString cKey = szWlanGuid;
                    CString cValue;
                    if (mapAdapters.Lookup(cKey, cValue))
                        mapAdapters.RemoveKey(szWlanGuid);
                }
            }
        }
        else
            _tprintf(_T("WlanEnumInterfaces failed with error: %u\n"), dwResult);

        WlanCloseHandle(hClient, NULL);
    }
    else
        _tprintf(_T("WlanOpenHandle failed with error: %u\n"), dwResult);
}

Use registry entries in order to distinguish between physical and virtual adapters.

C++
// Get network cards GUID from registry to give higher priority to physical adapters
// We aim to avoid virtual devices
CString sFriendlyName;
CRegKey oRegKey;
TCHAR szSubkey[1024];
DWORD dwNameLength;
CString sBaseKey = _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards");

if (ERROR_SUCCESS == oRegKey.Open(HKEY_LOCAL_MACHINE, sBaseKey, KEY_READ |
            KEY_ENUMERATE_SUB_KEYS))
{
    DWORD dwIndex = 0;
    while (TRUE)
    {
        dwNameLength = 1024;
        if (ERROR_NO_MORE_ITEMS == oRegKey.EnumKey
                (dwIndex++, szSubkey, &dwNameLength, NULL))
            break;

        CRegKey oRegKeyCard;
        if (ERROR_SUCCESS == oRegKeyCard.Open
        (HKEY_LOCAL_MACHINE, sBaseKey + _T("\\") + szSubkey, KEY_READ))
        {
            TCHAR szGuid[256];
            DWORD dwGuidLength = 256;
            CString cGuid;

            if (ERROR_SUCCESS == oRegKeyCard.QueryStringValue
            (_T("ServiceName"), szGuid, &dwGuidLength))
            {
                CString cValue;
                cGuid = szGuid;
                cGuid.Trim();
                if (mapAdapters.Lookup(cGuid, cValue))
                    sFriendlyName = cValue;
            }
            oRegKeyCard.Close();

            if (!sFriendlyName.IsEmpty())
                break;
        }
    }
    oRegKey.Close();
}

// No network adapter found in registry. Choose among the ones available
if (sFriendlyName.IsEmpty())
{
    CString sKey;
    POSITION pos = mapAdapters.GetStartPosition();
    if (pos != NULL)
        mapAdapters.GetNextAssoc(pos, sKey, sFriendlyName);
}

if (sFriendlyName.IsEmpty())
{
    _tprintf(_T("No ethernet adapter found\n"));
    return SNA_ERROR_NOADAPTERFOUND;
}

We have already chosen an adapter, so the last step is pretty straightforward. Just set IP, mask and gateway using netsh.

This last step requires administrator privileges:

C++
CString sIp = _T("192.168.1.2");
CString sMask = _T("255.255.255.0");
CString sGateway = _T("192.168.1.1");
CString sNetshIp;
sNetshIp.Format(_T("netsh interface ip set address name=\"%s\" static %s %s %s"),
sFriendlyName, sIp, sMask, sGateway);
sNetshIp.Trim();

CString sDns = _T("8.8.8.8");
CString sNetshDns;
sNetshDns.Format(_T("netsh interface ip set dns name=\"%s\" static %s"),
sFriendlyName, sDns);

// Set IP configuration of adapter
_tprintf(_T("Running: %s\n"), sNetshIp);
::ShellExecute(NULL, NULL, _T("cmd.exe"), CString(_T("/C ")) +
sNetshIp, NULL, SW_HIDE);
_tprintf(_T("Running: %s\n"), sNetshDns);
::ShellExecute(NULL, NULL, _T("cmd.exe"), CString(_T("/C ")) +
sNetshDns, NULL, SW_HIDE);

return 0;

References

  1. https://msdn.microsoft.com/en-us/library/windows/desktop/aa365915%28v=vs.85%29.aspx
  2. https://msdn.microsoft.com/en-us/library/windows/desktop/ms706716%28v=vs.85%29.aspx
  3. http://www.howtogeek.com/103190/change-your-ip-address-from-the-command-prompt/

History

  • February 29th, 2016 - Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)