cwindowsnetwork-programmingwinapi

Is there a documented WinAPI method to obtain static IP addresses of disconnected network adapters?


I am trying to programmatically obtain the same information as displayed by this command:

netsh interface ip show ipaddresses

Below is a function in C that accomplishes this goal in an undocumented (hence unstable) manner, but it works regardless of whether the network media is connected or not.

Is there a documented API method that can do this? I don't want to rely on undocumented code.

#include <windows.h>
#include <stdio.h>

int GetStaticIPs(void)
{
    HKEY hKey;
    LPCWSTR regPath = L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces";
    LONG result;

    // Open the registry key for network interfaces
    result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regPath, 0, KEY_READ, &hKey);
    if (result != ERROR_SUCCESS) {
        printf("Failed to open registry key: %ld\n", result);
        return 1;
    }

    // Enumerate subkeys (each subkey is an adapter GUID)
    DWORD index = 0;
    WCHAR subKeyName[255];
    DWORD subKeyNameSize = sizeof(subKeyName);
    while (RegEnumKeyEx(hKey, index, subKeyName, &subKeyNameSize, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
        HKEY hSubKey;
        WCHAR fullSubKeyPath[512];
        //_snwprintf(fullSubKeyPath, sizeof(fullSubKeyPath), L"%s\\%s", regPath, subKeyName);
        _snwprintf_s(fullSubKeyPath, sizeof(fullSubKeyPath), L"%s\\%s", regPath, subKeyName);

        // Open the adapter-specific subkey
        result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, fullSubKeyPath, 0, KEY_READ, &hSubKey);
        if (result == ERROR_SUCCESS) {
            // Check for static IP configuration
            BYTE ipData[1024];
            DWORD ipDataSize = sizeof(ipData);
            DWORD type;

            result = RegQueryValueEx(hSubKey, L"IPAddress", NULL, &type, ipData, &ipDataSize);
            if (result == ERROR_SUCCESS && type == REG_MULTI_SZ) {
                // REG_MULTI_SZ is a null-terminated string list, double-null terminated
                printf("\nAdapter GUID: %ls\n", subKeyName);
                PWCHAR ip = (PWCHAR)ipData;
                while (*ip) {
                    // Skip 0.0.0.0 (indicates DHCP or no static IP)
                    if (wcscmp(ip, L"0.0.0.0") != 0) {
                        printf("IPv4 Address: %ls\n", ip);
                    }
                    ip += wcslen(ip) + 1; // Move to next string
                }
            }
            else {
                // Check if DHCP is enabled (optional)
                DWORD dhcpEnabled;
                DWORD dhcpSize = sizeof(dhcpEnabled);
                result = RegQueryValueEx(hSubKey, L"EnableDHCP", NULL, NULL, (BYTE*)&dhcpEnabled, &dhcpSize);
                if (result == ERROR_SUCCESS && dhcpEnabled == 1) {
                    printf("\nAdapter GUID: %ls\n", subKeyName);
                    printf("Configured for DHCP\n");
                }
            }

            RegCloseKey(hSubKey);
        }
        subKeyNameSize = sizeof(subKeyName); // Reset size for next iteration
        index++;
    }

    RegCloseKey(hKey);
    return 0;
}

EDIT:

Using undocumented registry parsing is ugly, and since it is undocumented it is also unstable, i.e. it can change without notice. A stable and documented way would be better. Displaying network adapter's friendly name instead of an ugly GUID without a bloated code would be nice, too, albeit secondary.


Solution

  • It's possible to do this by calling GetIpAddrTable + GetIfTable2Ex :

    PMIB_IF_ROW2 GetRow(PMIB_IF_ROW2 Table, ULONG NumEntries, IF_INDEX dwIndex)
    {
        if (NumEntries)
        {
            do 
            {
                if (dwIndex == Table->InterfaceIndex)
                {
                    return Table;
                }
            } while (Table++,--NumEntries);
        }
    
        return 0;
    }
    
        ULONG dwSize = FIELD_OFFSET(MIB_IPADDRTABLE, table[2]);
    
        ULONG dwError;
        do 
        {
            dwError = ERROR_NOT_ENOUGH_MEMORY;
            if (PMIB_IPADDRTABLE pIpAddrTable = (PMIB_IPADDRTABLE)LocalAlloc(LMEM_FIXED, dwSize))
            {
                if (NOERROR == (dwError = GetIpAddrTable(pIpAddrTable, &dwSize, FALSE)))
                {
                    if (DWORD dwNumEntries = pIpAddrTable->dwNumEntries)
                    {
                        PMIB_IF_TABLE2 Table;
                        if (!GetIfTable2Ex(MibIfTableRaw, &Table))
                        {
                            PMIB_IPADDRROW table = pIpAddrTable->table;
                            do 
                            {
                                if (PMIB_IF_ROW2 row = GetRow(Table->Table, Table->NumEntries, table->dwIndex))
                                {
                                    char sz[16];
                                    RtlIpv4AddressToStringA((in_addr*)&table->dwAddr, sz);
                                    DbgPrint("%08x %-16s MTU=%u (%x.%x.%x) \"%ws\" \"%ws\"\n", 
                                        table->dwIndex, sz,
                                        row->Mtu, 
                                        row->InterfaceAndOperStatusFlags.HardwareInterface,
                                        row->InterfaceAndOperStatusFlags.ConnectorPresent,
                                        row->InterfaceAndOperStatusFlags.NotMediaConnected,
                                        row->Alias, row->Description);
                                }
    
                            } while (table++, --dwNumEntries);
                            FreeMibTable(Table);
                        }
                    }
                }
                LocalFree(pIpAddrTable);
            }
        } while (ERROR_INSUFFICIENT_BUFFER == dwError);
    

    Example of an output:

    
    
    0000000e 192.168.1.94     MTU=1500 (1.1.0) "Ethernet" "Realtek PCIe GBE Family Controller"
    00000008 192.168.75.1     MTU=1500 (0.0.0) "VMware Network Adapter VMnet1" "VMware Virtual Ethernet Adapter for VMnet1"
    00000015 192.168.141.1    MTU=1500 (0.0.0) "VMware Network Adapter VMnet8" "VMware Virtual Ethernet Adapter for VMnet8"
    00000001 127.0.0.1        MTU=1500 (0.0.0) "Loopback Pseudo-Interface 1" "Software Loopback Interface 1"