上一篇:WindowsAPI|每天了解几个winAPI接口之网络配置相关文档Iphlpapi.h详细分析9
如果有错误欢迎指正批评,在此只作为科普和参考。
C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\um\iphlpapi.h
文章目录
- GetNetworkParams:一次性拿到本机“全局固定”的 IPv4 网络参数——主机名、域名、DNS 服务器列表、节点类型、是否启用路由/代理等
- 1. 函数原型
- 2. 返回的核心结构 `FIXED_INFO`
- 3. 最小示例
- 4. 常见错误码
- 5. 典型用途
- 6. 限制与注意
- GetAdaptersInfo&GetAdapterOrderMap:把本机 所有 IPv4 网卡 的 完整信息一次性打包返回&给出 “连接顺序”(绑定顺序 / 路由优先级)里 各网卡接口索引 的先后列表。
- 一、GetAdaptersInfo
- 1. 原型
- 2. 返回的核心结构
- 3. 最小示例
- 二、GetAdapterOrderMap
- 1. 原型
- 2. 返回结构
- 3. 最小示例
- 三、何时用哪个?
- GetAdaptersAddresses:通过 Winsock2 API 获取本地网络适配器的地址信息
- ✅ 条件编译块
- ✅ Winsock2 依赖检查
- ✅ 函数声明
- ✅ 使用场景
- ✅ 示例代码(简化版)
- GetPerAdapterInfo:查某个网卡的 DHCP/WINS/DNS 服务器地址
- ✅ 条件编译
- ✅ 函数原型
- ✅ 作用
- ✅ 结构体定义(简要)
- ✅ 使用步骤(伪代码)
- ✅ 注意
- ✅ 总结一句话
- 网络接口硬件时间戳能力(hardware timestamping)
GetNetworkParams:一次性拿到本机“全局固定”的 IPv4 网络参数——主机名、域名、DNS 服务器列表、节点类型、是否启用路由/代理等
#pragma region Application Family or OneCore Family or Games Family
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)#if (NTDDI_VERSION >= NTDDI_WIN2KSP1)
IPHLPAPI_DLL_LINKAGE
DWORD
WINAPI
GetNetworkParams(_Out_writes_bytes_opt_(*pOutBufLen) PFIXED_INFO pFixedInfo,_Inout_ PULONG pOutBufLen);
#endif#endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES) */
#pragma endregion
GetNetworkParams
的作用是:
一次性拿到本机“全局固定”的 IPv4 网络参数——主机名、域名、DNS 服务器列表、节点类型、是否启用路由/代理等。
1. 函数原型
DWORD WINAPI GetNetworkParams(_Out_writes_bytes_opt_(*pOutBufLen) PFIXED_INFO pFixedInfo,_Inout_ PULONG pOutBufLen
);
- pFixedInfo
调用者提供的缓冲区;可传NULL
做“两次调用”套路:
第一次返回所需字节数,第二次真正取数据。 - pOutBufLen
输入时给出缓冲区大小,输出时返回实际需要的字节数。
2. 返回的核心结构 FIXED_INFO
typedef struct _FIXED_INFO {char HostName[MAX_HOSTNAME_LEN + 4];char DomainName[MAX_DOMAIN_NAME_LEN + 4];PIP_ADDR_STRING CurrentDnsServer; // 链表首节点IP_ADDR_STRING DnsServerList; // 第一个 DNS 地址UINT NodeType; // BROADCAST/PEER/HYBRID/MIXEDchar ScopeId[MAX_SCOPE_ID_LEN + 4];UINT EnableRouting; // 1=充当 IP 路由器UINT EnableProxy; // 1=启用 DNS 代理(废弃)UINT EnableDns; // 1=启用 DNS
} FIXED_INFO, *PFIXED_INFO;
- 所有字符串都是 ANSI(
char
)。 - DNS 服务器以 单向链表 形式挂接:
DnsServerList.Next → IP_ADDR_STRING*
。 EnableProxy
字段在 Win10 后已作废,始终为 0。
3. 最小示例
ULONG sz = 0;
GetNetworkParams(nullptr, &sz); // 第一次取大小
auto buf = std::make_unique<BYTE[]>(sz);
auto p = reinterpret_cast<PFIXED_INFO>(buf.get());DWORD ret = GetNetworkParams(p, &sz);
if (ret == NO_ERROR) {printf("Host : %s\n", p->HostName);printf("Domain: %s\n", p->DomainName);for (auto cur = &p->DnsServerList; cur; cur = cur->Next) {printf("DNS: %s\n", cur->IpAddress.String);}
} else {printf("GetNetworkParams failed: %lu\n", ret);
}
4. 常见错误码
值 | 含义 |
---|---|
ERROR_BUFFER_OVERFLOW | 缓冲区太小(第一次正常) |
ERROR_NO_DATA | TCP/IP 协议未安装 |
ERROR_INVALID_PARAMETER | 指针为空 |
5. 典型用途
- 网络诊断工具:快速列出本机 DNS 配置。
- DNS 切换器:确认当前主 DNS 地址后决定是否替换。
- 零配置服务:读取主机名、域名、节点类型做 NetBIOS 发现。
6. 限制与注意
- 仅 IPv4 参数;IPv6 DNS 需用
GetAdaptersAddresses
。 - 静态信息:不会实时更新 DHCP 续租后的变化,需重调一次。
- ANSI 字符串:若要 Unicode,需自行转换。
一句话总结
GetNetworkParams
就是“一键取本机 IPv4 全局网络名片”:主机名、域名、全部 DNS 服务器、节点类型等一次打包返回。
GetAdaptersInfo&GetAdapterOrderMap:把本机 所有 IPv4 网卡 的 完整信息一次性打包返回&给出 “连接顺序”(绑定顺序 / 路由优先级)里 各网卡接口索引 的先后列表。
#pragma region Desktop Family or OneCore Family or Games Family
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)IPHLPAPI_DLL_LINKAGE
ULONG
WINAPI
GetAdaptersInfo(_Out_writes_bytes_opt_(*SizePointer) PIP_ADAPTER_INFO AdapterInfo,_Inout_ PULONG SizePointer);IPHLPAPI_DLL_LINKAGE
PIP_ADAPTER_ORDER_MAP
WINAPI
GetAdapterOrderMap(VOID);#endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES) */
#pragma endregion
下面两个 API 都来自 iphlpapi.dll
,但用途完全不同。
一句话区分:
API | 一句话功能 |
---|---|
GetAdaptersInfo | 把本机 所有 IPv4 网卡 的 完整信息(MAC、IP、网关、DHCP、WINS…)一次性打包返回。 |
GetAdapterOrderMap | 给出 “连接顺序”(绑定顺序 / 路由优先级)里 各网卡接口索引 的先后列表。 |
一、GetAdaptersInfo
1. 原型
ULONG WINAPI GetAdaptersInfo(_Out_writes_bytes_opt_(*SizePointer) PIP_ADAPTER_INFO AdapterInfo,_Inout_ PULONG SizePointer
);
- 经典“两次调用”流程:
① 第一次把AdapterInfo
设NULL
,拿到所需字节数;
② 第二次真正取数据。 - 仅 IPv4;需要 IPv6 请用
GetAdaptersAddresses
。
2. 返回的核心结构
typedef struct _IP_ADAPTER_INFO {struct _IP_ADAPTER_INFO* Next; // 链表下一项DWORD ComboIndex; // 保留char AdapterName[MAX_ADAPTER_NAME_LENGTH + 4];char Description[MAX_ADAPTER_DESCRIPTION_LENGTH + 4];UINT AddressLength; // MAC 地址长度BYTE Address[MAX_ADAPTER_ADDRESS_LENGTH];// MAC 地址DWORD Index; // ifIndexUINT Type; // MIB_IF_TYPE_XXXUINT DhcpEnabled; // 1=DHCPPIP_ADDR_STRING CurrentIpAddress; // 当前地址IP_ADDR_STRING IpAddressList; // 所有 IPv4 地址链表IP_ADDR_STRING GatewayList; // 默认网关链表IP_ADDR_STRING DhcpServer; // DHCP 服务器BOOL HaveWins; // 是否启用 WINSIP_ADDR_STRING PrimaryWinsServer;IP_ADDR_STRING SecondaryWinsServer;time_t LeaseObtained;time_t LeaseExpires;
} IP_ADAPTER_INFO, *PIP_ADAPTER_INFO;
3. 最小示例
ULONG sz = 0;
GetAdaptersInfo(nullptr, &sz);
auto buf = std::make_unique<BYTE[]>(sz);
auto p = reinterpret_cast<PIP_ADAPTER_INFO>(buf.get());
if (GetAdaptersInfo(p, &sz) == ERROR_SUCCESS) {for (auto cur = p; cur; cur = cur->Next) {printf("Idx=%lu MAC=%02X-%02X-%02X-%02X-%02X-%02X Desc=%s\n",cur->Index,cur->Address[0], cur->Address[1], cur->Address[2],cur->Address[3], cur->Address[4], cur->Address[5],cur->Description);for (auto ip = &cur->IpAddressList; ip; ip = ip->Next)printf(" IPv4=%s/%s\n", ip->IpAddress.String, ip->IpMask.String);}
}
二、GetAdapterOrderMap
1. 原型
PIP_ADAPTER_ORDER_MAP WINAPI GetAdapterOrderMap(VOID);
- 直接返回一个 全局静态结构体指针,无需两次调用,无需释放。
- 失败返回
NULL
。
2. 返回结构
typedef struct _IP_ADAPTER_ORDER_MAP {ULONG NumAdapters;ULONG AdapterOrder[1]; // 柔性数组,NumAdapters 个 ifIndex
} IP_ADAPTER_ORDER_MAP, *PIP_ADAPTER_ORDER_MAP;
AdapterOrder[0]
是绑定顺序 最高(默认路由最先匹配)的接口;
依次递减。- 与
netsh interface ipv4 show interfaces level=verbose
里看到的 “InterfaceMetric” 顺序一致。
3. 最小示例
auto order = GetAdapterOrderMap();
if (order) {for (ULONG i = 0; i < order->NumAdapters; ++i)printf("Order[%lu] = IfIndex %lu\n", i, order->AdapterOrder[i]);
}
三、何时用哪个?
需求 | 推荐 API |
---|---|
枚举 IPv4 网卡、MAC、IP、网关、DHCP、WINS | GetAdaptersInfo |
需要 IPv6、DNS、Gateway、DNS suffix、MTU 等更全信息 | GetAdaptersAddresses |
只想知道“连接顺序”/路由优先级 | GetAdapterOrderMap |
一句话总结
- GetAdaptersInfo 像“网卡体检报告”;
- GetAdapterOrderMap 像“路由优先级排行榜”。
GetAdaptersAddresses:通过 Winsock2 API 获取本地网络适配器的地址信息
#pragma region Application Family or OneCore Family or Games Family
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)#ifdef _WINSOCK2API_//
// The following functions require Winsock2.
//IPHLPAPI_DLL_LINKAGE
ULONG
WINAPI
GetAdaptersAddresses(_In_ ULONG Family,_In_ ULONG Flags,_Reserved_ PVOID Reserved,_Out_writes_bytes_opt_(*SizePointer) PIP_ADAPTER_ADDRESSES AdapterAddresses,_Inout_ PULONG SizePointer);#endif
它的作用是通过 Winsock2 API 获取本地网络适配器的地址信息。
解释一下这段代码的结构和含义:
✅ 条件编译块
#pragma region Application Family or OneCore Family or Games Family
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
- 这是为了控制函数在不同 Windows 平台(如桌面、UWP、Xbox、HoloLens 等)上的可见性。
- 只有目标平台支持
APP
、SYSTEM
或GAMES
分区时,这段代码才会被编译。
✅ Winsock2 依赖检查
#ifdef _WINSOCK2API_
- 只有在包含了
<winsock2.h>
(即_WINSOCK2API_
被定义)时,GetAdaptersAddresses
才会被声明。 - 避免与早期 Winsock 1.x 冲突。
✅ 函数声明
IPHLPAPI_DLL_LINKAGE
ULONG
WINAPI
GetAdaptersAddresses(_In_ ULONG Family,_In_ ULONG Flags,_Reserved_ PVOID Reserved,_Out_writes_bytes_opt_(*SizePointer) PIP_ADAPTER_ADDRESSES AdapterAddresses,_Inout_ PULONG SizePointer
);
这是 GetAdaptersAddresses
的标准 C 接口声明:
参数名 | 含义 |
---|---|
Family | 地址族,如 AF_INET (IPv4)、AF_INET6 (IPv6)或 AF_UNSPEC (两者都包括) |
Flags | 控制返回信息的类型,如是否包括 DNS 后缀、是否跳过回环接口等 |
Reserved | 保留参数,必须为 NULL |
AdapterAddresses | 输出缓冲区,用于接收 IP_ADAPTER_ADDRESSES 结构体链表 |
SizePointer | 输入输出参数,传入缓冲区大小,返回实际所需大小 |
✅ 使用场景
你可以用 GetAdaptersAddresses
来获取本机的网络接口信息,比如:
- IP 地址(IPv4/IPv6)
- MAC 地址
- DNS 服务器
- 网关
- 接口描述、类型、状态等
✅ 示例代码(简化版)
#include <winsock2.h>
#include <iphlpapi.h>
#include <iostream>#pragma comment(lib, "iphlpapi.lib")int main() {ULONG size = 0;GetAdaptersAddresses(AF_UNSPEC, 0, nullptr, nullptr, &size);PIP_ADAPTER_ADDRESSES adapters = (PIP_ADAPTER_ADDRESSES)malloc(size);if (GetAdaptersAddresses(AF_UNSPEC, 0, nullptr, adapters, &size) == ERROR_SUCCESS) {for (auto* p = adapters; p; p = p->Next) {std::wcout << L"Adapter: " << p->FriendlyName << std::endl;}}free(adapters);return 0;
}
GetPerAdapterInfo:查某个网卡的 DHCP/WINS/DNS 服务器地址
#if (NTDDI_VERSION >= NTDDI_WIN2KSP1)
IPHLPAPI_DLL_LINKAGE
DWORD
WINAPI
GetPerAdapterInfo(_In_ ULONG IfIndex,_Out_writes_bytes_opt_(*pOutBufLen) PIP_PER_ADAPTER_INFO pPerAdapterInfo,_Inout_ PULONG pOutBufLen);
#endif#endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES) */
#pragma endregion
它和 GetAdaptersAddresses
一样,也受平台分区(App / System / Games)和 Windows 版本(NTDDI_WIN2KSP1 及以上)的限制。
✅ 条件编译
#if (NTDDI_VERSION >= NTDDI_WIN2KSP1)
- 只有目标系统的 SDK 版本 ≥ Windows 2000 SP1 时,这段声明才会被编译。
- 防止在老系统上调用不存在的 API。
✅ 函数原型
IPHLPAPI_DLL_LINKAGE
DWORD
WINAPI
GetPerAdapterInfo(_In_ ULONG IfIndex, // 接口索引_Out_writes_bytes_opt_(*pOutBufLen) PIP_PER_ADAPTER_INFO pPerAdapterInfo,_Inout_ PULONG pOutBufLen // 缓冲区大小/返回所需大小
);
✅ 作用
获取 指定网络接口(由 IfIndex 标识) 的“每适配器”信息,主要包括:
- DHCP 服务器地址
- 主/备 WINS 服务器地址
- 是否启用 DHCP / WINS
✅ 结构体定义(简要)
typedef struct _IP_PER_ADAPTER_INFO {UINT autoconfigEnabled; // 是否启用自动配置UINT autoconfigActive; // 是否正在使用自动配置PIP_ADDR_STRING currentDnsServer; // 当前 DNS 服务器IP_ADDR_STRING dnsServerList; // DNS 服务器链表UINT nodeType; // 节点类型(B、P、M、H)SCOPE_ID scopeId; // NetBIOS scope IDUINT enableRouting; // 是否启用路由UINT enableProxy; // 是否启用代理UINT enableDns; // 是否启用 DNS
} IP_PER_ADAPTER_INFO, *PIP_PER_ADAPTER_INFO;
✅ 使用步骤(伪代码)
ULONG idx = 5; // 假设已知接口索引
ULONG size = 0;
GetPerAdapterInfo(idx, nullptr, &size); // 先拿大小
auto* info = (PIP_PER_ADAPTER_INFO)malloc(size);
if (GetPerAdapterInfo(idx, info, &size) == ERROR_SUCCESS) {printf("DHCP server: %s\n", info->dhcpServer.IpAddress.String);
}
free(info);
✅ 注意
IfIndex
必须通过GetAdaptersInfo
或GetAdaptersAddresses
提前拿到。- 该 API 不支持 IPv6,只针对 IPv4 接口。
- 返回的 DNS 服务器列表是 单链表,需要遍历
dnsServerList.Next
。
✅ 总结一句话
GetPerAdapterInfo
是“老派”IPv4 专用接口,用来查某个网卡的 DHCP/WINS/DNS 服务器地址;新项目建议优先用 GetAdaptersAddresses
,它覆盖 IPv4/IPv6 且信息更全面。
网络接口硬件时间戳能力(hardware timestamping)
#pragma region Desktop Family or OneCore Family or Games Family
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)#if (NTDDI_VERSION >= NTDDI_WIN10_FE)typedef struct _INTERFACE_HARDWARE_TIMESTAMP_CAPABILITIES
{BOOLEAN PtpV2OverUdpIPv4EventMessageReceive;BOOLEAN PtpV2OverUdpIPv4AllMessageReceive;BOOLEAN PtpV2OverUdpIPv4EventMessageTransmit;BOOLEAN PtpV2OverUdpIPv4AllMessageTransmit;BOOLEAN PtpV2OverUdpIPv6EventMessageReceive;BOOLEAN PtpV2OverUdpIPv6AllMessageReceive;BOOLEAN PtpV2OverUdpIPv6EventMessageTransmit;BOOLEAN PtpV2OverUdpIPv6AllMessageTransmit;BOOLEAN AllReceive;BOOLEAN AllTransmit;BOOLEAN TaggedTransmit;
} INTERFACE_HARDWARE_TIMESTAMP_CAPABILITIES, *PINTERFACE_HARDWARE_TIMESTAMP_CAPABILITIES;typedef struct _INTERFACE_SOFTWARE_TIMESTAMP_CAPABILITIES
{BOOLEAN AllReceive;BOOLEAN AllTransmit;BOOLEAN TaggedTransmit;
} INTERFACE_SOFTWARE_TIMESTAMP_CAPABILITIES, *PINTERFACE_SOFTWARE_TIMESTAMP_CAPABILITIES;typedef struct _INTERFACE_TIMESTAMP_CAPABILITIES
{ULONG64 HardwareClockFrequencyHz;BOOLEAN SupportsCrossTimestamp;INTERFACE_HARDWARE_TIMESTAMP_CAPABILITIES HardwareCapabilities;INTERFACE_SOFTWARE_TIMESTAMP_CAPABILITIES SoftwareCapabilities;
} INTERFACE_TIMESTAMP_CAPABILITIES, *PINTERFACE_TIMESTAMP_CAPABILITIES;typedef struct _INTERFACE_HARDWARE_CROSSTIMESTAMP
{ULONG64 SystemTimestamp1;ULONG64 HardwareClockTimestamp;ULONG64 SystemTimestamp2;
} INTERFACE_HARDWARE_CROSSTIMESTAMP, *PINTERFACE_HARDWARE_CROSSTIMESTAMP;DECLARE_HANDLE(HIFTIMESTAMPCHANGE);
这段是 Windows SDK 10.0.19041(Win10 FE=“Fall Edition”,即 20H1)以后才出现的 网络接口硬件时间戳能力(hardware timestamping) 相关定义。它只会在:
- 目标平台分区包含 Desktop / System / Games
- 且 SDK 版本 ≥ NTDDI_WIN10_FE(20H1,10.0.19041)
时才会被编译器看到。
下面把几个结构体按“谁干什么用”快速梳理一下。
────────────────────────
- 能力描述结构体
────────────────────────
INTERFACE_HARDWARE_TIMESTAMP_CAPABILITIES
INTERFACE_SOFTWARE_TIMESTAMP_CAPABILITIES
INTERFACE_TIMESTAMP_CAPABILITIES
- 通过 GetAdaptersAddresses 的新标志 GAA_FLAG_INCLUDE_TIMESTAMP 返回,位于 IP_ADAPTER_ADDRESSES::HardwareTimestampCapabilities 字段。
- 用来告诉调用者:
‑ 该 NIC 的硬件时钟频率(HardwareClockFrequencyHz)
‑ 是否支持“交叉时间戳”(见下文)
‑ 支持哪些 PTP(IEEE 1588 v2)报文类型、UDP/IPv4 还是 IPv6、RX 还是 TX、是否支持“Tagged”时间戳(VLAN/QoS 标记)
‑ 软件时间戳能力(内核软件栈能否打时间戳)
────────────────────────
2. 交叉时间戳结构体
────────────────────────
INTERFACE_HARDWARE_CROSSTIMESTAMP
- 用于把 系统 QPC(QueryPerformanceCounter) 与 NIC 本地硬件时钟 做一次“同步采样”。
- 通过新的 IOCTL IOCTL_NSI_GET_CROSS_TIMESTAMP(或未来 SDK 暴露的 API)一次性返回 3 个 64-bit 值:
SystemTime1 → HardwareClock → SystemTime2 - 应用程序用简单线性插值就可以把 NIC 时间戳线性地映射到系统时间,实现亚毫秒级同步。
────────────────────────
3. 句柄声明
────────────────────────
DECLARE_HANDLE(HIFTIMESTAMPCHANGE);
- 这是一个不透明句柄,用于注册“时间戳能力变化”通知(类似网络状态变化回调)。
- 目前公开文档中尚未给出配套的 API 原型,估计未来会和 NotifyInterfaceTimestampChange 之类的新函数一起放出。
────────────────────────
4. 小结(一句话)
────────────────────────
这些定义是 Win10 20H1+ 新增的 NIC 硬件时间戳能力 与 系统-硬件时钟同步 接口,专为高精度时间同步(PTP、金融交易、工业控制、5G)场景服务;普通应用不会用到,但如果你要写 PTP 客户端或者做纳秒级测量的驱动/服务,就得关心它们。