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.
Using the IP Helper API function GetAdaptersAddresses()
does not work because this function returns 0 in PIP_ADAPTER_ADDRESSES->FirstUnicastAddress
for disconnected network adapters (i.e. adapters with disconnected media, a.k.a. "unplugged") regardless whether the IP address of the adapter is statically configured or not. GetAdaptersAddresses()
works only for connected network adapters.
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"