XP 系统上获取网卡IP和子网掩码信息
2025-01-23 20:23:32

GetAdaptersAddresses

对于 Vista 后的系统,获取网卡信息用 GetAdaptersAddresses 函数就够了,支持查询所有网卡、支持IPv6等。
但是对于 XP 系统,GetAdaptersAddresses得到的结构体会少了些东西,在 XP 上IP_ADAPTER_ADDRESSES被映射到IP_ADAPTER_ADDRESSES_XP

1
2
3
4
5
6
7
#if (NTDDI_VERSION >= NTDDI_VISTA)
typedef IP_ADAPTER_ADDRESSES_LH IP_ADAPTER_ADDRESSES;
typedef IP_ADAPTER_ADDRESSES_LH *PIP_ADAPTER_ADDRESSES;
#elif (NTDDI_VERSION >= NTDDI_WINXP)
typedef IP_ADAPTER_ADDRESSES_XP IP_ADAPTER_ADDRESSES;
typedef IP_ADAPTER_ADDRESSES_XP *PIP_ADAPTER_ADDRESSES;
#else

看看两者的差别:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
typedef struct _IP_ADAPTER_ADDRESSES_XP {
union {
ULONGLONG Alignment;
struct {
ULONG Length;
DWORD IfIndex;
};
};
struct _IP_ADAPTER_ADDRESSES_XP *Next;
PCHAR AdapterName;
PIP_ADAPTER_UNICAST_ADDRESS_XP FirstUnicastAddress;
PIP_ADAPTER_ANYCAST_ADDRESS_XP FirstAnycastAddress;
PIP_ADAPTER_MULTICAST_ADDRESS_XP FirstMulticastAddress;
PIP_ADAPTER_DNS_SERVER_ADDRESS_XP FirstDnsServerAddress;
PWCHAR DnsSuffix;
PWCHAR Description;
PWCHAR FriendlyName;
BYTE PhysicalAddress[MAX_ADAPTER_ADDRESS_LENGTH];
DWORD PhysicalAddressLength;
DWORD Flags;
DWORD Mtu;
DWORD IfType;
IF_OPER_STATUS OperStatus;
DWORD Ipv6IfIndex;
DWORD ZoneIndices[16];
PIP_ADAPTER_PREFIX_XP FirstPrefix;
} IP_ADAPTER_ADDRESSES_XP,
*PIP_ADAPTER_ADDRESSES_XP;

typedef struct _IP_ADAPTER_ADDRESSES_LH {
union {
ULONGLONG Alignment;
struct {
ULONG Length;
IF_INDEX IfIndex;
};
};
struct _IP_ADAPTER_ADDRESSES_LH *Next;
PCHAR AdapterName;
PIP_ADAPTER_UNICAST_ADDRESS_LH FirstUnicastAddress;
PIP_ADAPTER_ANYCAST_ADDRESS_XP FirstAnycastAddress;
PIP_ADAPTER_MULTICAST_ADDRESS_XP FirstMulticastAddress;
PIP_ADAPTER_DNS_SERVER_ADDRESS_XP FirstDnsServerAddress;
PWCHAR DnsSuffix;
PWCHAR Description;
PWCHAR FriendlyName;
BYTE PhysicalAddress[MAX_ADAPTER_ADDRESS_LENGTH];
ULONG PhysicalAddressLength;
union {
ULONG Flags;
struct {
ULONG DdnsEnabled : 1;
ULONG RegisterAdapterSuffix : 1;
ULONG Dhcpv4Enabled : 1;
ULONG ReceiveOnly : 1;
ULONG NoMulticast : 1;
ULONG Ipv6OtherStatefulConfig : 1;
ULONG NetbiosOverTcpipEnabled : 1;
ULONG Ipv4Enabled : 1;
ULONG Ipv6Enabled : 1;
ULONG Ipv6ManagedAddressConfigurationSupported : 1;
};
};
ULONG Mtu;
IFTYPE IfType;
IF_OPER_STATUS OperStatus;
IF_INDEX Ipv6IfIndex;
ULONG ZoneIndices[16];
PIP_ADAPTER_PREFIX_XP FirstPrefix;

ULONG64 TransmitLinkSpeed;
ULONG64 ReceiveLinkSpeed;
PIP_ADAPTER_WINS_SERVER_ADDRESS_LH FirstWinsServerAddress;
PIP_ADAPTER_GATEWAY_ADDRESS_LH FirstGatewayAddress;
ULONG Ipv4Metric;
ULONG Ipv6Metric;
IF_LUID Luid;
SOCKET_ADDRESS Dhcpv4Server;
NET_IF_COMPARTMENT_ID CompartmentId;
NET_IF_NETWORK_GUID NetworkGuid;
NET_IF_CONNECTION_TYPE ConnectionType;
TUNNEL_TYPE TunnelType;
//
// DHCP v6 Info.
//
SOCKET_ADDRESS Dhcpv6Server;
BYTE Dhcpv6ClientDuid[MAX_DHCPV6_DUID_LENGTH];
ULONG Dhcpv6ClientDuidLength;
ULONG Dhcpv6Iaid;
#if (NTDDI_VERSION >= NTDDI_VISTASP1)
PIP_ADAPTER_DNS_SUFFIX FirstDnsSuffix;
#endif
} IP_ADAPTER_ADDRESSES_LH,
*PIP_ADAPTER_ADDRESSES_LH;

其中子网掩码在FirstUnicastAddress字段,这个字段在 XP 上也映射到了不同结构体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
typedef struct _IP_ADAPTER_UNICAST_ADDRESS_LH {
union {
ULONGLONG Alignment;
struct {
ULONG Length;
DWORD Flags;
};
};
struct _IP_ADAPTER_UNICAST_ADDRESS_LH *Next;
SOCKET_ADDRESS Address;

IP_PREFIX_ORIGIN PrefixOrigin;
IP_SUFFIX_ORIGIN SuffixOrigin;
IP_DAD_STATE DadState;

ULONG ValidLifetime;
ULONG PreferredLifetime;
ULONG LeaseLifetime;
UINT8 OnLinkPrefixLength;
} IP_ADAPTER_UNICAST_ADDRESS_LH,

typedef struct _IP_ADAPTER_UNICAST_ADDRESS_XP {
union {
ULONGLONG Alignment;
struct {
ULONG Length;
DWORD Flags;
};
};
struct _IP_ADAPTER_UNICAST_ADDRESS_XP *Next;
SOCKET_ADDRESS Address;

IP_PREFIX_ORIGIN PrefixOrigin;
IP_SUFFIX_ORIGIN SuffixOrigin;
IP_DAD_STATE DadState;

ULONG ValidLifetime;
ULONG PreferredLifetime;
ULONG LeaseLifetime;
} IP_ADAPTER_UNICAST_ADDRESS_XP, *PIP_ADAPTER_UNICAST_ADDRESS_XP;

其中OnLinkPrefixLength就是网卡的子网掩码,但是 XP 系统上没有这个字段,不过还有其他补救方法。

GetAdaptersInfo

GetAdaptersInfo 用于获取所有活动网卡信息。
它可以获取网卡的IP地址、子网掩码、MAC地址信息,其中IP地址和子网掩码以字符串形式返回,如果想要int类型的话需要自行处理。
一个要求 Vista+ 的转换实现:

1
2
3
4
5
SOCKADDR_IN addr;
INT addrLen = sizeof(SOCKADDR_IN);
WSAStringToAddressA(p->IpAddressList.IpAddress.String, AF_INET, NULL, (LPSOCKADDR)&addr, &addrLen);
unsigned long ipInt = addr.sin_addr.s_addr; // 网络字节序
ipInt = ntohl(ipInt); // 转为主机字节序,这个函数要求Vista+

更简单的版本:

1
2
3
4
5
unsigned ip_str_to_u32(const char* ip) {
unsigned a, b, c, d;
sscanf(ip, "%u.%u.%u.%u", &a, &b, &c, &d);
return (a << 24) | (b << 16) | (c << 8) | d;
}

GetIpAddrTable

GetIpAddrTable 可以获取系统上的 IP 地址表,会得到一个 MIB_IPADDRROW_XP 结构列表,其中dwMask字段就是当前 IP 地址的掩码,但是并不知道 IP 属于哪个网卡,此时有两种选择:

  1. 再次调用 GetIfEntry 函数得到其网卡信息(其中还有MAC地址)。
  2. 先用GetAdaptersAddresses获取网卡列表,再用GetIpAddrTable获取 IP 地址表,然后比较两者的 IP 地址或dwIndex字段。


这要调用两个函数,不推荐。

其他相关函数

总结

要在 XP 系统上枚举网卡并得到IP、子网掩码、MAC地址信息的话用 GetAdaptersInfo 就够了。
不需要支持 XP 系统的话用更新的 GetAdaptersAddresses 函数。

相关阅读

list adapters addresses and mask/prefix, both IPv4 and IPv6 (GetAdaptersAddresses)