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.
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"