当前位置: 首页 > news >正文

WindowsAPI|每天了解几个winAPI接口之网络配置相关文档Iphlpapi.h详细分析八

上一篇:WindowsAPI|每天了解几个winAPI接口之网络配置相关文档Iphlpapi.h详细分析七
如果有错误欢迎指正批评,在此只作为科普和参考。

C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\um\iphlpapi.h

文章目录

  • SetTcpEntry:强制断开(reset)一条已存在的 IPv4 TCP 连接
      • 1. 函数原型
      • 2. MIB_TCPROW 关键字段
      • 3. 典型使用步骤
      • 4. 最小示例
      • 5. 注意事项
  • 什么是五元组?
      • 举例
      • 为什么重要
  • GetInterfaceInfo: 拿到本机所有“具备 IPv4 能力的网络接口”的一张简表
      • 函数原型
      • 返回的 `IP_INTERFACE_INFO`
      • 典型用法(两步)
      • 常见错误码
      • 与 GetIfTable 的区别
  • GetUniDirectionalAdapterInfo:枚举本机中所有 仅支持“单向发送”(Unidirectional / Send-only)的 IPv4 网络接口
      • 1. 什么是“单向发送”接口?
      • 2. 函数原型
      • 3. 返回的数据结构
      • 4. 典型用法
      • 5. 常见返回值
  • NhpAllocateAndGetInterfaceInfoFromStack:在指定的堆上分配一块内存,把“接口+名字+索引”的完整表拷出来,并返回元素个数(未公开函数)
      • 1. 作用一句话
      • 2. 函数签名
      • 3. 返回的数据结构
      • 4. 典型内部用法(仅供理解)
      • 5. 风险与建议
  • GetBestInterface:给定一个目标 IPv4 地址,告诉我本机应该把包从哪块网卡发出去
      • 1. 函数原型
      • 2. 工作原理
      • 3. 最小示例
      • 4. 常见用途
      • 5. 与相关 API 的区别
  • 一些预处理器指令
      • 1. 关键宏
      • 2. 语句逐句拆解
      • 3. 对开发者的影响

SetTcpEntry:强制断开(reset)一条已存在的 IPv4 TCP 连接

//////////////////////////////////////////////////////////////////////////////
//                                                                          //
// Used to set the state of a TCP Connection. The only state that it can be //
// set to is MIB_TCP_STATE_DELETE_TCB.  The complete MIB_TCPROW structure   //
// MUST BE SPECIFIED                                                        //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////IPHLPAPI_DLL_LINKAGE
DWORD
WINAPI
SetTcpEntry(_In_ PMIB_TCPROW pTcpRow);

SetTcpEntry 用来强制断开(reset)一条已存在的 IPv4 TCP 连接,其唯一合法操作就是把连接状态设为 MIB_TCP_STATE_DELETE_TCB(数值 12)。
换句话说,它等价于在命令行里对某个套接字执行 tcpkillnetsh interface ipv4 delete address,但粒度精确到“一条五元组”。


1. 函数原型

DWORD WINAPI SetTcpEntry(_In_ PMIB_TCPROW pTcpRow   // 必须完整填写一条现有 TCP 连接的信息
);
  • 返回值
    • NO_ERROR (0) —— 连接已被内核立即拆除
    • ERROR_INVALID_PARAMETER —— 结构填写不完整或状态不是 DELETE_TCB
    • ERROR_NOT_FOUND —— 找不到对应五元组的连接
    • ERROR_ACCESS_DENIED —— 权限不足(需管理员)

2. MIB_TCPROW 关键字段

typedef struct _MIB_TCPROW {DWORD dwState;      // 必须设为 MIB_TCP_STATE_DELETE_TCB (12)DWORD dwLocalAddr;  // 本地 IPv4 地址(网络字节序)DWORD dwLocalPort;  // 本地端口(网络字节序)DWORD dwRemoteAddr; // 远端 IPv4 地址(网络字节序)DWORD dwRemotePort; // 远端端口(网络字节序)DWORD dwOwningPid;  // 拥有该套接字的进程 PID(可填 0 表示不关心)
} MIB_TCPROW, *PMIB_TCPROW;
  • 五个字段(协议固定为 TCP,不在结构里出现)共同构成 五元组;必须和 GetExtendedTcpTableGetTcpTable 返回的数据完全一致才能匹配成功。

3. 典型使用步骤

  1. GetExtendedTcpTable(或旧版 GetTcpTable)枚举所有 TCP 连接,找到目标连接。
  2. 把对应的 完整 MIB_TCPROW 拷出来,仅把 dwState 改成 MIB_TCP_STATE_DELETE_TCB
  3. 调用 SetTcpEntry(&row)
  4. 内核立即向两端发送 RST,连接进入 TIME_WAIT → 消失。

4. 最小示例

#include <iphlpapi.h>
#pragma comment(lib, "iphlpapi.lib")// 假设已用 GetExtendedTcpTable 取得要断开的连接 row
MIB_TCPROW killRow = {0};
killRow.dwState      = MIB_TCP_STATE_DELETE_TCB;
killRow.dwLocalAddr  = inet_addr("192.168.1.50");
killRow.dwLocalPort  = htons(54321);
killRow.dwRemoteAddr = inet_addr("203.0.113.7");
killRow.dwRemotePort = htons(80);
killRow.dwOwningPid  = 0;   // 不校验 PIDDWORD err = SetTcpEntry(&killRow);
if (err == NO_ERROR)printf("Connection killed.\n");
elseprintf("SetTcpEntry failed: %lu\n", err);

5. 注意事项

  • 仅 IPv4 TCP;IPv6 需用 SetTcpEntry2 + MIB_TCP6ROW
  • 本地连接才能被本机拆除;不能跨主机操作。
  • 对监听套接字(dwState = LISTEN)调用会关闭监听端口。
  • 某些安全软件或防火墙驱动可能会拦截 RST,导致看似“杀死”失败。

一句话总结:
SetTcpEntry 是“管理员级”的 TCP 连接杀手——只要给出精确的五元组,就能把那条连接瞬间 reset 掉。

什么是五元组?

在网络通信里,五元组(5-tuple)就是一条连接在传输层用来唯一标识自己的 五个字段

  1. 源 IP 地址
  2. 源端口号
  3. 目的 IP 地址
  4. 目的端口号
  5. 传输层协议(TCP、UDP、ICMP、SCTP …)

只要这五个值都相同,系统就认为它们是同一条流(flow / connection);任何一个值不同,就是另一条独立流。


举例

192.168.1.50  54321  →  203.0.113.7  80  TCP
  • 源 IP:192.168.1.50
  • 源端口:54321
  • 目的 IP:203.0.113.7
  • 目的端口:80
  • 协议:TCP

这五个字段一起构成了这条 HTTP 会话的五元组。


为什么重要

  • NAT/防火墙 用五元组做会话表项。
  • 负载均衡 按五元组做哈希分流。
  • TCP 连接跟踪(Linux conntrack、Windows TCPIP.sys)都拿它当主键。
  • 刚才的 SetTcpEntry 也必须提供准确的五元组才能定位到那一条 TCP 连接。

一句话:
五元组就是“一条网络连接的身份证明”。

GetInterfaceInfo: 拿到本机所有“具备 IPv4 能力的网络接口”的一张简表

IPHLPAPI_DLL_LINKAGE
DWORD
WINAPI
GetInterfaceInfo(_Out_writes_bytes_opt_(*dwOutBufLen) PIP_INTERFACE_INFO  pIfTable,_Inout_                        PULONG              dwOutBufLen);

GetInterfaceInfo 用来拿到本机 所有“具备 IPv4 能力的网络接口”的一张简表,表里只包含接口编号和对应的 友好名称(即注册表里 Name 字段,形如 “Ethernet”、“Wi-Fi”)。
它不会像 GetIfTable/GetIfEntry2 那样给出速率、MTU、统计等详细信息,而是“最小可打印列表”。


函数原型

DWORD WINAPI GetInterfaceInfo(_Out_writes_bytes_opt_(*dwOutBufLen) PIP_INTERFACE_INFO pIfTable,_Inout_ PULONG dwOutBufLen
);
  • pIfTable
    指向调用者分配的缓冲区;如果传 NULL,函数会把 所需字节数 写到 *dwOutBufLen 并返回 ERROR_INSUFFICIENT_BUFFER,这是典型的“两次调用”套路。

  • dwOutBufLen
    输入时表示 pIfTable 缓冲区大小;输出时返回实际需要的字节数。


返回的 IP_INTERFACE_INFO

typedef struct _IP_INTERFACE_INFO {LONG  NumAdapters;            // 表中条目数IP_ADAPTER_INDEX_MAP Adapter[1]; // 柔性数组
} IP_INTERFACE_INFO, *PIP_INTERFACE_INFO;typedef struct _IP_ADAPTER_INDEX_MAP {ULONG Index;      // 接口索引(ifIndex)WCHAR Name[MAX_ADAPTER_NAME]; // Unicode 友好名,如 L"Ethernet"
} IP_ADAPTER_INDEX_MAP;
  • 只列出 IPv4 已启用的接口;禁用的、无 IPv4 的不会返回。
  • 名称是本地化的,比如中文系统可能是 “以太网”。

典型用法(两步)

ULONG bufLen = 0;
GetInterfaceInfo(nullptr, &bufLen);           // 第一次:拿大小
auto pInfo = (PIP_INTERFACE_INFO)new BYTE[bufLen];
DWORD ret = GetInterfaceInfo(pInfo, &bufLen); // 第二次:真正取数据
if (ret == NO_ERROR) {for (LONG i = 0; i < pInfo->NumAdapters; ++i) {wprintf(L"Index=%lu  Name=%s\n",pInfo->Adapter[i].Index,pInfo->Adapter[i].Name);}
}
delete[] (BYTE*)pInfo;

常见错误码

含义
ERROR_INSUFFICIENT_BUFFER提供的缓冲区太小(第一次调用时正常)
ERROR_NO_DATA本机没有 IPv4 接口
ERROR_NOT_SUPPORTED系统版本不支持(极旧)

与 GetIfTable 的区别

API返回内容接口类型名称
GetInterfaceInfo索引 + 友好名仅 IPv4 启用接口正确 Unicode 名称
GetIfTable索引 + 类型 + MTU + 统计所有接口内部描述符(可能不是用户友好名)

一句话总结:
GetInterfaceInfo 就是“给我一个干净列表,告诉我这台机器有哪些 IPv4 网卡、它们各自的索引和友好名字”。

GetUniDirectionalAdapterInfo:枚举本机中所有 仅支持“单向发送”(Unidirectional / Send-only)的 IPv4 网络接口

IPHLPAPI_DLL_LINKAGE
DWORD
WINAPI
GetUniDirectionalAdapterInfo(_Out_writes_bytes_opt_(*dwOutBufLen) PIP_UNIDIRECTIONAL_ADAPTER_ADDRESS pIPIfInfo,_Inout_                        PULONG                             dwOutBufLen);

GetUniDirectionalAdapterInfo 用来枚举本机中所有 仅支持“单向发送”(Unidirectional / Send-only)的 IPv4 网络接口,并给出这些接口各自绑定的 IPv4 地址。


1. 什么是“单向发送”接口?

  • 只能 向外发包不能接收 入站流量;
  • 通常见于:
    − 卫星上行链路、某些单向广播网卡;
    − 早期电视回传通道、单向 GPRS 发射模块;
    − 为了安全而人为做成“只写”的实验或军工网络。
  • 在 Windows 里这类接口会被标记 IF_TYPE_OTHER 且驱动声明 接收能力为 0

2. 函数原型

DWORD WINAPI GetUniDirectionalAdapterInfo(_Out_writes_bytes_opt_(*dwOutBufLen)PIP_UNIDIRECTIONAL_ADAPTER_ADDRESS pIPIfInfo,_Inout_ PULONG dwOutBufLen
);
  • pIPIfInfo
    指向调用者分配的缓冲区;可传 NULL 做“两次调用”套路:第一次返回所需字节数。
  • dwOutBufLen
    输入时给出缓冲区大小;输出时返回实际需要的字节数。

3. 返回的数据结构

typedef struct _IP_UNIDIRECTIONAL_ADAPTER_ADDRESS {ULONG  NumAdapters;          // 单向接口数量IPAddr Address[1];           // 柔性数组,每项是 in_addr 值(网络字节序)
} IP_UNIDIRECTIONAL_ADAPTER_ADDRESS, *PIP_UNIDIRECTIONAL_ADAPTER_ADDRESS;
  • 只有 IPv4 地址;没有接口索引、没有掩码、没有名称。
  • 如果本机没有这种适配器,NumAdapters == 0,函数返回 ERROR_NO_DATA

4. 典型用法

ULONG bufLen = 0;
GetUniDirectionalAdapterInfo(nullptr, &bufLen);   // 第一次拿大小
auto pInfo = (PIP_UNIDIRECTIONAL_ADAPTER_ADDRESS)new BYTE[bufLen];
DWORD ret = GetUniDirectionalAdapterInfo(pInfo, &bufLen);
if (ret == NO_ERROR) {for (ULONG i = 0; i < pInfo->NumAdapters; ++i) {struct in_addr in; in.S_un.S_addr = pInfo->Address[i];printf("Send-only adapter: %s\n", inet_ntoa(in));}
} else if (ret == ERROR_NO_DATA) {printf("No unidirectional adapters.\n");
}
delete[] (BYTE*)pInfo;

5. 常见返回值

含义
NO_ERROR (0)成功
ERROR_INSUFFICIENT_BUFFER缓冲区太小
ERROR_NO_DATA本机没有单向发送接口
ERROR_NOT_SUPPORTED极老系统未实现

一句话总结:
GetUniDirectionalAdapterInfo 就是“告诉我这台电脑有哪些只能发不能收的网卡,以及它们现在绑定的 IPv4 地址”。

NhpAllocateAndGetInterfaceInfoFromStack:在指定的堆上分配一块内存,把“接口+名字+索引”的完整表拷出来,并返回元素个数(未公开函数)

#if (NTDDI_VERSION >= NTDDI_WIN2KSP1)
#ifndef NHPALLOCATEANDGETINTERFACEINFOFROMSTACK_DEFINED
#define NHPALLOCATEANDGETINTERFACEINFOFROMSTACK_DEFINEDIPHLPAPI_DLL_LINKAGE
DWORD
WINAPI
NhpAllocateAndGetInterfaceInfoFromStack(_Outptr_ IP_INTERFACE_NAME_INFO  **ppTable,_Out_       PDWORD                  pdwCount,_In_        BOOL                    bOrder,_In_        HANDLE                  hHeap,_In_        DWORD                   dwFlags);#endif
#endif // (NTDDI_VERSION >= NTDDI_WIN2KSP1)

NhpAllocateAndGetInterfaceInfoFromStack 是一个 iphlpapi.dll 的未公开(undocumented / internal)辅助函数,从 Windows 2000 SP1 开始存在,仅供 TCP/IP 协议栈内部或微软自家组件调用,不保证向后兼容,也不应被第三方代码直接使用


1. 作用一句话

在指定的堆上分配一块内存,把“接口+名字+索引”的完整表拷出来,并返回元素个数。


2. 函数签名

DWORD WINAPI NhpAllocateAndGetInterfaceInfoFromStack(_Outptr_ IP_INTERFACE_NAME_INFO **ppTable,  // 返回的表首地址_Out_    PDWORD                   pdwCount, // 数组元素个数_In_     BOOL                     bOrder,   // TRUE = 按接口索引升序_In_     HANDLE                   hHeap,    // 要使用的堆句柄_In_     DWORD                    dwFlags   // 0 或 HEAP_GENERATE_EXCEPTIONS
);

3. 返回的数据结构

typedef struct _IP_INTERFACE_NAME_INFO {DWORD Index;                // 接口索引 (ifIndex)DWORD MediaType;            // NDIS_MEDIUM 值,如 NdisMedium802_3UCHAR Name[MAX_ADAPTER_NAME]; // ANSI/UTF-8 接口名ULONG NameLength;           // 名字实际长度UCHAR Pad[4];               // 对齐填充
} IP_INTERFACE_NAME_INFO, *PIP_INTERFACE_NAME_INFO;
  • 数组长度 = *pdwCount
  • 内存由函数在 hHeap 上分配,调用方负责 HeapFree(hHeap, 0, *ppTable)

4. 典型内部用法(仅供理解)

HANDLE hHeap = GetProcessHeap();
IP_INTERFACE_NAME_INFO *pTable;
DWORD cnt = 0;DWORD err = NhpAllocateAndGetInterfaceInfoFromStack(&pTable, &cnt, TRUE, hHeap, 0);
if (err == NO_ERROR) {for (DWORD i = 0; i < cnt; ++i) {printf("Idx=%lu  Name=%s\n",pTable[i].Index, pTable[i].Name);}HeapFree(hHeap, 0, pTable);
}

5. 风险与建议

  • 未公开:函数名以 Nhp(Network Helper Private)开头,随时可能改名或消失。
  • 替代 API:公开、稳定的版本请用
    • GetInterfaceInfo(仅 IPv4)
    • GetAdaptersAddresses(推荐,IPv4/IPv6、GUID、友好名、网关、DNS 等一网打尽)。

一句话总结:
NhpAllocateAndGetInterfaceInfoFromStack 是 Windows 内部用的“堆上分配接口表”私有函数;它能用,但不应该用——写正式代码请改用公开且长期兼容的 GetAdaptersAddresses/GetInterfaceInfo

GetBestInterface:给定一个目标 IPv4 地址,告诉我本机应该把包从哪块网卡发出去

IPHLPAPI_DLL_LINKAGE
DWORD
WINAPI
GetBestInterface(_In_  IPAddr  dwDestAddr,_Out_ PDWORD  pdwBestIfIndex);#endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES) */
#pragma endregion

GetBestInterface 就是一句话:
“给定一个目标 IPv4 地址,告诉我本机应该把包从哪块网卡发出去。”


1. 函数原型

DWORD WINAPI GetBestInterface(_In_  IPAddr  dwDestAddr,   // 目标 IPv4 地址(网络字节序)_Out_ PDWORD pdwBestIfIndex // 返回最佳接口的索引
);
  • 返回值
    • NO_ERROR (0) —— 成功
    • ERROR_INVALID_PARAMETER —— 地址全 0 或全 1
    • ERROR_NO_DATA —— 找不到可用路由(例如路由表为空或所有路由都失效)

2. 工作原理

内核按如下顺序决策:

  1. 本地 IPv4 路由表(与 route print 看到的一致)。
  2. 选出“最长前缀匹配”+“最低 Metric”的那条路由。
  3. 该路由所绑定的接口索引就是“最佳接口”。

如果目标就是本机地址,返回 loopback 接口(通常是 Index = 1)。


3. 最小示例

#include <iphlpapi.h>
#pragma comment(lib, "iphlpapi.lib")DWORD BestIdx;
DWORD ret = GetBestInterface(inet_addr("8.8.8.8"), &BestIdx);
if (ret == NO_ERROR)printf("Best interface for 8.8.8.8 = %lu\n", BestIdx);
elseprintf("GetBestInterface failed: %lu\n", ret);

4. 常见用途

  • 网络诊断工具:快速判断流量会走哪块网卡。
  • VPN/代理程序:在插入路由前,先探测“真实”出口。
  • 负载均衡:动态选择最优链路。

5. 与相关 API 的区别

API额外信息协议族
GetBestInterface仅返回接口索引IPv4
GetBestInterfaceEx同时可返回 IPv6IPv4/IPv6
GetBestRoute返回完整 MIB_IPFORWARDROW(网关、掩码等)IPv4

一句话总结:
GetBestInterface 让程序在代码里做“路由查询”——给定目标 IP,立刻知道数据包会从哪块网卡出去。

一些预处理器指令


#endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES) */
#pragma endregion#pragma region Application Family or OneCore Family or Games Family
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)

这些 #pragma region … #endif 只是 Windows SDK 中的“分区”(partition) 预处理器指令,用来把同一份头文件切成 不同 API 可见性范围,从而:

  • UWP/商店应用传统 Win32 桌面程序Xbox/游戏驱动/系统组件 各自只看到 自己允许调用的 API
  • 防止你在 UWP 里误用了仅限 桌面 的函数(编译期就会报错)。

1. 关键宏

含义
WINAPI_PARTITION_DESKTOP经典 Win32 桌面/服务进程
WINAPI_PARTITION_APPUWP/商店应用
WINAPI_PARTITION_SYSTEMNT 服务、驱动、系统组件
WINAPI_PARTITION_GAMESXbox、游戏主机或 Win32 游戏

2. 语句逐句拆解

#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | \WINAPI_PARTITION_SYSTEM | \WINAPI_PARTITION_GAMES)/* 这之间的声明只对桌面/系统/游戏可见,UWP 看不到 */
#endif /* WINAPI_FAMILY_PARTITION(...) */#pragma region Application Family or OneCore Family or Games Family
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | \WINAPI_PARTITION_SYSTEM | \WINAPI_PARTITION_GAMES)/* 这之间的声明 UWP + 系统 + 游戏可见,传统桌面也能看到 */
#endif
  • 逻辑是“位掩码或”——只要 当前工程设置的 WINAPI_FAMILY 与任一掩码匹配,代码就保留;否则整段被预处理器剔除。
  • 在 VS 工程里,WINAPI_FAMILY目标平台 自动设定:
    • 桌面 EXE → WINAPI_FAMILY_DESKTOP_APP
    • UWP → WINAPI_FAMILY_PC_APP
    • Xbox → WINAPI_FAMILY_GAMES

3. 对开发者的影响

  • 写桌面程序:这些宏基本透明,你无需理会。
  • 写 UWP:如果编译提示 “未声明标识符”,大概率因为该 API 被分区保护;只能换公开给 WINAPI_PARTITION_APP 的函数。
  • 查看头文件:遇到 #pragma region 就知道“下面这段 API 仅对某类应用开放”。

一句话总结:
它们只是 微软用来“按平台裁剪 API”的开关,确保不同形态的应用程序只能调用自己被允许的函数,防止越权。

http://www.dtcms.com/a/346411.html

相关文章:

  • Mixture of Experts Guided by Gaussian Splatters Matters
  • Python 调用 sora_image模型 API 实现图片生成与垫图
  • 判断一个字母是 ​大写字母​ 还是 ​小写字母
  • [RestGPT] OpenAPI规范(OAS)
  • 容器安全实践(一):概念篇 - 从“想当然”到“真相”
  • Go语言延迟初始化(Lazy Initialization)最佳实践指南
  • 通过构建大规模动态神经回路模型,揭示了静息态人脑皮层存在层次结构
  • JCTools 并发无锁链表队列 LinkedQueue
  • 洛谷P3370字符串哈希(集合:Hash表)
  • Ubuntu解决makefile交叉编译的问题
  • 提升用户体验的交互设计实战指南:方法、流程与技巧
  • 在通义灵码中配置MCP服务
  • Linux--进程核心概念
  • 基于SamGeo模型和地图客户端的实时图形边界提取
  • 把 AI 变成「会思考的路灯」——基于自学习能耗模型的智慧路灯杆
  • Open3d:点对点ICP配准,点对面ICP配准
  • 105.QML实现现代Neumorphism风格界面01-Button实现
  • 如何提升科研能力:先停止“无效工作”,开始“有效科研”
  • 第二节阶段WinFrom-5:文件操作
  • 车载诊断架构 --- EOL引起关于DTC检测开始条件的思考
  • Linux822 shell:expect 批量
  • 《C++起源与核心:版本演进+命名空间法》
  • 易基因:Nat Commun/IF15.7:多组学研究揭示UHRF2在原始生殖细胞DNA甲基化重编程中的抗性调控机制
  • 光耦合器:电子世界的 “光桥梁“
  • Opnecv详细介绍
  • 量子计算基础
  • C#_组合优于继承的实际应用
  • 音视频处理工作室:实时通信的媒体层设计
  • 容器操作案例
  • C语言——内存函数