ReactOS 网络 IP 地址设置系统配置窗口分析
目录
- 概述
- 总体架构与调用链
- 入口:ncpa.cpl 网络连接控制面板
- netshell.dll 网络连接外壳扩展
- netcfgx.dll 网络配置核心
- TCP/IP 属性对话框实现(tcpipconf_notify.c)
- 属性页对话框资源
- 注册表数据存储
- 应用变更的运行时路径(到内核 tcpip.sys)
- 完整调用链图
- 关键文件索引
1. 概述
ReactOS 中,用户配置网络 IP 地址的系统配置窗口由三个模块协作完成:
| 模块 | 作用 | 源码位置 |
|---|---|---|
| ncpa.cpl | 控制面板"网络连接"小程序入口 | [dll/cpl/ncpa](file:///d:/reactos/dll/cpl/ncpa) |
| netshell.dll | 网络连接外壳扩展:文件夹视图、右键菜单、连接属性对话框 | [dll/shellext/netshell](file:///d:/reactos/dll/shellext/netshell) |
| netcfgx.dll | 网络配置核心 COM 库:INetCfg 体系 + TCP/IP 属性对话框(IP 地址/子网掩码/网关/DNS 的设置与保存) | [dll/win32/netcfgx](file:///d:/reactos/dll/win32/netcfgx) |
功能路径:用户在"网络连接"文件夹中对某个连接(如"本地连接")点右键 → “属性”,在弹出的属性窗口中选择"Internet 协议 (TCP/IP)“再点"属性”,即可打开**"Internet 协议 (TCP/IP) 属性"对话框**,填写 IP 地址、子网掩码、默认网关、DNS 服务器。点击"确定"后,配置被写入注册表并通过 IPHLPAPI 实时应用到内核 tcpip.sys 协议栈。
用户操作路径:
控制面板 → 网络连接 (ncpa.cpl)
→ 右键连接 → 属性 (netshell: shfldr_netconnect.cpp)
→ 连接属性对话框 (netshell: lanconnectui.cpp, IDD_NETPROPERTIES)
→ 选中 "Internet 协议 (TCP/IP)" → 属性
→ INetCfgComponent::RaisePropertyUi (netcfgx)
→ TCP/IP 属性对话框 (netcfgx: tcpipconf_notify.c)
→ 写入注册表 Tcpip\Parameters\Interfaces\{GUID}
→ AddIPAddress/DeleteIPAddress → tcpip.sys 生效
2. 总体架构与调用链
┌─────────────────────────────────────────────────────────────────────┐
│ 用户模式 (Ring 3) │
│ │
│ ┌───────────────┐ ┌─────────────────────────────────────────┐ │
│ │ ncpa.cpl │ │ netshell.dll (外壳扩展) │ │
│ │ (控制面板) │────▶│ CNetworkConnections (文件夹视图) │ │
│ │ DisplayApplet │ │ └─ IContextMenu "属性" 动词 │ │
│ └───────────────┘ │ └─ ShowNetConnectionProperties │ │
│ │ └─ CNetConnectionPropertyUi │ │
│ │ (连接属性属性表) │ │
│ └───────────────┬─────────────────────────┘ │
│ │ RaisePropertyUi (COM) │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ netcfgx.dll (网络配置核心) │ │
│ │ ┌──────────────────────────────────────────────────────┐ │ │
│ │ │ CLSID_CNetCfg → INetCfg (netcfg_iface.c) │ │ │
│ │ │ CLSID_TcpipConfigNotifyObject → TcpipConfNotifyImpl │ │ │
│ │ │ ├─ INetCfgComponentControl (初始化/应用/取消) │ │ │
│ │ │ ├─ INetCfgComponentPropertyUi (属性页) │ │ │
│ │ │ ├─ INetCfgComponentSetup (安装/升级/移除) │ │ │
│ │ │ └─ ITcpipProperties (查询属性) │ │ │
│ │ └──────────────────────────────────────────────────────┘ │ │
│ └───────────────┬───────────────────────────────────────────────┘ │
│ │ │
│ RegSetValueExW (注册表) AddIPAddress/DeleteIPAddress │
│ │ (IPHLPAPI → TDI/IOCTL) │
└──────────────────┼────────────────────────────────────┬────────────┘
▼ ▼
HKLM\SYSTEM\CurrentControlSet\ \Device\Tcp (tcpip.sys)
Services\Tcpip\Parameters\Interfaces\{GUID} └→ 见《tcpip网络接口层分析》
(EnableDHCP/IPAddress/SubnetMask/
DefaultGateway/NameServer/...)
三个关键衔接点:
| 衔接点 | 内容 |
|---|---|
| 外壳 → COM | netshell 通过 INetCfgComponent::RaisePropertyUi(NCRP_SHOW_PROPERTY_UI) 触发 netcfgx 的属性页 |
| UI → 注册表 | netcfgx 通过 INetCfgComponentControl::ApplyRegistryChanges() 把设置写入 Tcpip\Parameters\Interfaces\{GUID} |
| 注册表 → 内核 | INetCfgComponentControl::ApplyPnpChanges() 调用 IPHLPAPI 的 AddIPAddress/DeleteIPAddress/CreateIpForwardEntry 等,经 TDI/IOCTL 进入 tcpip.sys |
3. 入口:ncpa.cpl 网络连接控制面板
源码:[ncpa.c](file:///d:/reactos/dll/cpl/ncpa/ncpa.c)
ncpa.cpl 本身是一个非常薄的壳,双击时并不自己绘制窗口,而是通过 ShellExecute 打开资源管理器中的"网络连接"系统文件夹(CLSID 路径):
LONG CALLBACK
DisplayApplet(VOID)
{
WCHAR szParameters[] = L"/n,::{20D04FE0-3AEA-1069-A2D8-08002B30309D}"
L"\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}"
L"\\::{7007ACC7-3202-11D1-AAD2-00805FC1270E}";
/* NOTE: If Explorer shell is not available, use filebrowser.exe instead */
return (INT_PTR) ShellExecuteW(NULL, L"open",
GetShellWindow() ? L"explorer.exe"
: L"filebrowser.exe",
szParameters, NULL, SW_SHOWDEFAULT) > 32;
}
CLSID 路径解析:
{20D04FE0-...}= 我的电脑(My Computer){21EC2020-...}= 控制面板(Control Panel){7007ACC7-3202-11D1-AAD2-00805FC1270E}= 网络连接文件夹(Network Connections,由 netshell.dll 注册)
CPlApplet 入口函数仅处理 CPL_INIT / CPL_GETCOUNT(1) / CPL_DBLCLK,符合标准控制面板小程序协议。构建目标见 [CMakeLists.txt](file:///d:/reactos/dll/cpl/ncpa/CMakeLists.txt)。
4. netshell.dll 网络连接外壳扩展
netshell.dll 注册了网络连接系统文件夹({7007ACC7-...}),并实现其中的视图与交互。
4.1 文件夹视图与右键菜单 ([shfldr_netconnect.cpp](file:///d:/reactos/dll/shellext/netshell/shfldr_netconnect.cpp))
CNetworkConnections:文件夹视图对象(IShellFolder、IShellView),枚举系统中的网络连接。CNetConUiObject::InvokeCommand():处理上下文菜单命令。当用户对连接右键选择"属性"时调用:
HRESULT WINAPI CNetConUiObject::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
{
...
return ShowNetConnectionProperties(pCon, lpcmi->hwnd);
}
ShowNetConnectionProperties():获取连接的NETCON_PROPERTIES,创建CNetConnectionPropertyUi对象并调用其AddPages(),把连接属性页加入属性表:
pinfo.pszCaption = pProperties->pszwName; // 窗口标题 = 连接名
hr = pNCP->AddPages(hwnd, PropSheetExCallback, (LPARAM)&pinfo);
4.2 连接属性对话框 ([lanconnectui.cpp](file:///d:/reactos/dll/shellext/netshell/lanconnectui.cpp))
CNetConnectionPropertyUi 实现 INetConnectionPropertyUi 与 INetLanConnectionUiInfo。
a) 属性页主体 IDD_NETPROPERTIES(LANPropertiesUIDlg)
对话框内含一个组件列表 IDC_COMPONENTSLIST(列出"客户端、服务、协议"三类网络组件),由 EnumComponents() 填充:
VOID CNetConnectionPropertyUi::EnumComponents(HWND hDlgCtrl, INetCfg *pNCfg,
const GUID *CompGuid, UINT Type, ...)
每个列表项保存 PNET_ITEM,其中 pNCfgComp 指向对应的 INetCfgComponent COM 对象。
b) 打开 TCP/IP 属性(ShowNetworkComponentProperties)
用户双击组件或点击"属性"按钮后,获取选中项的 INetCfgComponent 并触发属性 UI:
pItem = (PNET_ITEM)lvItem.lParam;
pNCfgComp = (INetCfgComponent*)pItem->pNCfgComp;
hr = pNCfgComp->RaisePropertyUi(GetParent(hwndDlg), NCRP_QUERY_PROPERTY_UI, (INetConnectionConnectUi*)this);
if (SUCCEEDED(hr))
{
hr = pNCfgComp->RaisePropertyUi(GetParent(hwndDlg), NCRP_SHOW_PROPERTY_UI, (INetConnectionConnectUi*)this);
}
NCRP_SHOW_PROPERTY_UI 请求最终落到 netcfgx.dll 的 TCP/IP 配置对象上(RaisePropertyUi 由 netcfgx 的 INetCfgComponent::RaisePropertyUi 实现,见 [inetcfgcomp_iface.c](file:///d:/reactos/dll/win32/netcfgx/inetcfgcomp_iface.c))。
INetLanConnectionUiInfo::GetDeviceGuid()([lanconnectui.cpp](file:///d:/reactos/dll/shellext/netshell/lanconnectui.cpp#L504-L510))向 netcfgx 提供当前连接的设备 GUID,netcfgx 据此定位对应的适配器设置。
4.3 其他功能
setup.cpp:CoCreateInstance(CLSID_CNetCfg)获取INetCfg,加写锁 →Initialize→QueryNetCfgClass→Install→Apply,用于网络组件的安装向导。lanstatusui.cpp:连接状态 UI(lanstatusui.h中定义了状态相关资源)。connectmanager.cpp:通过NetCfgInstanceId注册表值把设备与连接对象关联。
5. netcfgx.dll 网络配置核心
5.1 DLL 结构与 COM 类工厂 ([netcfgx.c](file:///d:/reactos/dll/win32/netcfgx/netcfgx.c))
netcfgx.dll 注册两个 COM 类,通过接口表分发:
const GUID CLSID_TcpipConfigNotifyObject =
{0xA907657F, 0x6FDF, 0x11D0, {0x8E, 0xFB, 0x00, 0xC0, 0x4F, 0xD9, 0x12, 0xB2}};
static INTERFACE_TABLE InterfaceTable[] =
{
{ &CLSID_CNetCfg, INetCfg_Constructor }, // netcfg_iface.c
{ &CLSID_TcpipConfigNotifyObject, TcpipConfigNotify_Constructor }, // tcpipconf_notify.c
{ NULL, NULL }
};
| CLSID | 构造器 | 用途 |
|---|---|---|
CLSID_CNetCfg | INetCfg_Constructor | 核心网络配置对象(INetCfg 体系) |
CLSID_TcpipConfigNotifyObject | TcpipConfigNotify_Constructor | TCP/IP 配置通知对象(实现 IP 设置对话框) |
5.2 文件职责清单
| 文件 | 职责 |
|---|---|
| [netcfgx.c](file:///d:/reactos/dll/win32/netcfgx/netcfgx.c) | DLL 入口、类工厂、DllRegisterServer |
| [netcfg_iface.c](file:///d:/reactos/dll/win32/netcfgx/netcfg_iface.c) | INetCfg 主接口:Initialize/Apply/Cancel/QueryNetCfgClass;Apply() → ApplyOrCancelChanges() 遍历各组件类调用控制接口 |
| [inetcfgcomp_iface.c](file:///d:/reactos/dll/win32/netcfgx/inetcfgcomp_iface.c) | INetCfgComponent:RaisePropertyUi(触发属性页)、EnumBindingPaths 等 |
| [netcfgclass_iface.c](file:///d:/reactos/dll/win32/netcfgx/netcfgclass_iface.c) | INetCfgClass:枚举/选择组件 |
| [netcfgbindinginterface_iface.c](file:///d:/reactos/dll/win32/netcfgx/netcfgbindinginterface_iface.c) | INetCfgBindingInterface:绑定接口 |
| [netcfgbindingpath_iface.c](file:///d:/reactos/dll/win32/netcfgx/netcfgbindingpath_iface.c) | INetCfgBindingPath:绑定路径 |
| [installer.c](file:///d:/reactos/dll/win32/netcfgx/installer.c) | NetClassInstaller(DIF_INSTALLDEVICE 类安装器)、InstallNetDevice 通过 setupapi 安装网卡设备 |
| [netinstall.c](file:///d:/reactos/dll/win32/netcfgx/netinstall.c) | 网络组件安装辅助 |
| [propertypage.c](file:///d:/reactos/dll/win32/netcfgx/propertypage.c) | NetPropPageProvider:网卡设备的 Ndi\Params 属性页(设备高级属性) |
| [tcpipconf_notify.c](file:///d:/reactos/dll/win32/netcfgx/tcpipconf_notify.c) | TCP/IP 配置对象:IP 设置属性对话框 + 注册表读写 + PnP 应用(核心) |
5.3 INetCfg::Apply 的分发机制 ([netcfg_iface.c](file:///d:/reactos/dll/win32/netcfgx/netcfg_iface.c#L698-L840))
ApplyOrCancelChanges(BOOL bApply)
{
...
if (bApply)
{
INetCfgComponentControl_ApplyRegistryChanges(pHead->pControl);
INetCfgComponentControl_ApplyPnpChanges(pHead->pControl, pCallback);
}
}
...
INetCfg_fnApply(...)
{
ApplyOrCancelChanges(This->pNet, &GUID_DEVCLASS_NET, TRUE, ...);
ApplyOrCancelChanges(This->pClient, &GUID_DEVCLASS_NETCLIENT, TRUE, ...);
ApplyOrCancelChanges(This->pService, &GUID_DEVCLASS_NETSERVICE, TRUE, ...);
ApplyOrCancelChanges(This->pProtocol, &GUID_DEVCLASS_NETTRANS, TRUE, ...);
}
Apply() 遍历网络类(网卡/客户端/服务/协议)的所有组件,对每个组件调用 ApplyRegistryChanges()(写注册表)和 ApplyPnpChanges()(运行时重配内核)。TCP/IP 协议组件的这两步分别实现为 [tcpipconf_notify.c](file:///d:/reactos/dll/win32/netcfgx/tcpipconf_notify.c#L4070-L4402) 中的 INetCfgComponentControl_fnApplyRegistryChanges 与 INetCfgComponentControl_fnApplyPnpChanges。
6. TCP/IP 属性对话框实现(tcpipconf_notify.c)
[tcpipconf_notify.c](file:///d:/reactos/dll/win32/netcfgx/tcpipconf_notify.c)(约 4680 行)是整个 IP 设置窗口的核心,实现 TcpipConfNotifyImpl 对象。
6.1 核心数据结构
typedef struct tagIP_ADDR // 单个 IP 配置项(链表节点)
{
DWORD IpAddress; // IP 地址
union
{
DWORD SubnetMask; // 子网掩码
USHORT Metric; // 网关跃点数
} u;
ULONG NTEContext; // 内核网络表项上下文(AddIPAddress 返回)
struct tagIP_ADDR *Next; // 支持多 IP/多网关/多 DNS
} IP_ADDR;
typedef struct _AdapterSettings // 单个网卡的全部 TCP/IP 设置
{
...
WCHAR AdapterName[45]; // 注册表接口名(GUID 字符串)
GUID AdapterGuid; // 设备 GUID
DWORD Index; // 接口索引
IP_ADDR *OldIp; // 修改前的 IP 链表
IP_ADDR *NewIp; // 修改后的 IP 链表
IP_ADDR *OldGw; *NewGw; // 网关(含 Metric)
IP_ADDR *OldNs; *NewNs; // DNS 服务器
UINT OldDhcpEnabled; UINT NewDhcpEnabled; // 是否 DHCP
UINT DnsDhcpEnabled; // DNS 是否随 DHCP
DWORD OldMetric; DWORD NewMetric; // 接口跃点数
DWORD OldRegisterAdapterName; DWORD NewRegisterAdapterName;
DWORD OldRegistrationEnabled; DWORD NewRegistrationEnabled;
WCHAR szDomain[100]; // DNS 后缀
LPWSTR szTCPAllowedPorts; szUDPAllowedPorts; szRawIPAllowedProtocols; // 安全筛选
...
AlternateConfiguration AltConfig; // 备用配置(APIPA/用户配置)
} AdapterSettings;
typedef struct // COM 对象主体:多接口聚合
{
const INetCfgComponentControl *lpVtbl; // IUnknown + 控制
const INetCfgComponentPropertyUi *lpVtblCompPropertyUi;// 属性页
const INetCfgComponentSetup *lpVtblCompSetup; // 安装
const ITcpipProperties *lpVtblTcpipProperties; // 属性查询
LONG ref;
INetCfg *pNCfg; INetCfgComponent *pNComp; // 宿主 INetCfg
TcpipSettings *pTcpIpSettings; // 全局 TCP/IP 参数
AdapterSettings *pAdapterListHead, *pAdapterListTail; // 全部网卡设置链表
AdapterSettings *pCurrentAdapter; // 当前编辑的网卡
} TcpipConfNotifyImpl;
设计要点:
- Old/New 双份数据:对话框编辑的是
New*,Old*保留原值,应用时通过IpAddressesChanged()/GatewaysChanged()/NameServersChanged()比较差异,只写有变化的项,并支持取消。 - 单链表多地址:
IP_ADDR通过Next串联,Windows 原生支持一个网卡多个 IP/网关/DNS。 - 多网卡并存:
Initialize()用GetAdaptersInfo()枚举所有适配器,逐个LoadAdapterSettings()构建AdapterSettings双向链表。
6.2 实现的四个 COM 接口
| 接口 | 关键函数 | 作用 |
|---|---|---|
INetCfgComponentControl | Initialize / ApplyRegistryChanges / ApplyPnpChanges / CancelChanges | 初始化、写注册表、运行时应用、取消 |
INetCfgComponentPropertyUi | SetContext / MergePropPages / ValidateProperties / ApplyProperties | 属性页创建与上下文绑定 |
INetCfgComponentSetup | Install / Upgrade / ReadAnswerFile / Removing | 安装/升级/应答文件/移除(当前均为空实现) |
ITcpipProperties | ITcpipProperties_fnUnknown1 | 按 GUID 查询适配器 IP 设置字符串(供状态 UI 使用) |
6.3 属性页的创建(MergePropPages)
[INetCfgComponentPropertyUi_fnMergePropPages](file:///d:/reactos/dll/win32/netcfgx/tcpipconf_notify.c#L3801-L3846):
if (This->pCurrentAdapter->NewDhcpEnabled)
NumPages = 2; // DHCP 模式:常规 + 备用配置
else
NumPages = 1; // 静态模式:仅常规
hppages[0] = InitializePropertySheetPage(MAKEINTRESOURCEW(IDD_TCPIP_BASIC_DLG),
TcpipBasicDlg, (LPARAM)This, NULL);
if (NumPages == 2)
{
hppages[1] = InitializePropertySheetPage(MAKEINTRESOURCEW(IDD_TCPIP_ALTCF_DLG),
TcpipAltConfDlg, (LPARAM)This, NULL);
}
页数根据是否 DHCP 动态调整:
- 静态 IP 模式:1 页(常规)
- DHCP 模式:2 页(常规 + 备用配置)
- 点"常规"页的"高级"按钮 → [LaunchAdvancedTcpipSettings](file:///d:/reactos/dll/win32/netcfgx/tcpipconf_notify.c#L2477-L2514) 再弹出 3 页的高级属性表(IP 设置 / DNS / 选项)
6.4 上下文绑定(SetContext)
[INetCfgComponentPropertyUi_fnSetContext](file:///d:/reactos/dll/win32/netcfgx/tcpipconf_notify.c#L3426-L3476):
netcfgx 通过 INetLanConnectionUiInfo::GetDeviceGuid() 拿到当前连接的设备 GUID,然后遍历 pAdapterListHead 按 AdapterName(GUID 字符串)匹配,设置 This->pCurrentAdapter。这样同一个 COM 对象可以在多个网卡的属性对话框中复用。
6.5 设置读取(Load 流程)
Initialize()([tcpipconf_notify.c](file:///d:/reactos/dll/win32/netcfgx/tcpipconf_notify.c#L3745-L3799)):
INetCfgComponentControl_fnInitialize
└─ Initialize(This)
├─ GetAdaptersInfo() 枚举所有网卡 (IPHLPAPI)
├─ LoadTcpipSettings() 读全局参数 (Tcpip\Parameters)
│ ├─ UseDomainNameDevolution
│ ├─ SearchList
│ └─ EnableSecurityFilters
└─ 对每个网卡: LoadAdapterSettings() (Tcpip\Parameters\Interfaces\{GUID})
├─ DhcpEnabled → 来自 IP_ADAPTER_INFO
├─ CopyIpAddrString() → NewIp/OldIp (IP + 子网掩码)
├─ CopyIpAddrString() → NewGw/OldGw (网关 + 跃点数)
├─ ParseNameServer() → NewNs/OldNs (注册表 NameServer 值)
├─ InterfaceMetric → 接口跃点数
├─ RegisterAdapterName / RegistrationEnabled
├─ Domain → DNS 后缀
├─ TCP/UDP/RawIP 端口筛选 → 安全筛选
└─ ActiveConfigurations → 备用配置 (DHCP\Configurations\Alternate_{GUID})
6.6 设置写入(ApplyRegistryChanges)
[INetCfgComponentControl_fnApplyRegistryChanges](file:///d:/reactos/dll/win32/netcfgx/tcpipconf_notify.c#L4070-L4255) 按差异写注册表:
| 注册表值 | 类型 | 写入条件 |
|---|---|---|
InterfaceMetric | REG_DWORD | Metric 变化 |
RegisterAdapterName / RegistrationEnabled | REG_DWORD | 变化时 |
Domain | REG_SZ | 总是 |
EnableDHCP | REG_DWORD | DHCP 开关变化或 IP 变化 |
IPAddress / SubnetMask | REG_MULTI_SZ | 静态模式;DHCP 模式写 "0.0.0.0" |
DefaultGateway / DefaultGatewayMetric | REG_MULTI_SZ | 网关变化 |
NameServer | REG_SZ | DNS 变化(空则删除) |
UseDomainNameDevolution / SearchList / EnableSecurityFilters | - | 全局参数 |
关键片段(写 IP 地址,支持多地址 REG_MULTI_SZ):
if ((pAdapter->NewDhcpEnabled != pAdapter->OldDhcpEnabled) ||
IpAddressesChanged(pAdapter))
{
RegSetValueExW(hKey, L"EnableDHCP", 0, REG_DWORD, &pAdapter->NewDhcpEnabled, sizeof(DWORD));
if (pAdapter->NewDhcpEnabled)
{
RegSetValueExW(hKey, L"IPAddress", 0, REG_MULTI_SZ, (LPBYTE)L"0.0.0.0\0", 9 * sizeof(WCHAR));
RegSetValueExW(hKey, L"SubnetMask", 0, REG_MULTI_SZ, (LPBYTE)L"0.0.0.0\0", 9 * sizeof(WCHAR));
}
else
{
pStr = CreateMultiSzString(pAdapter->NewIp, IPADDR, &dwSize, FALSE);
RegSetValueExW(hKey, L"IPAddress", 0, REG_MULTI_SZ, (LPBYTE)pStr, dwSize);
pStr = CreateMultiSzString(pAdapter->NewIp, SUBMASK, &dwSize, FALSE);
RegSetValueExW(hKey, L"SubnetMask", 0, REG_MULTI_SZ, (LPBYTE)pStr, dwSize);
}
}
6.7 运行时应用(ApplyPnpChanges)
[INetCfgComponentControl_fnApplyPnpChanges](file:///d:/reactos/dll/win32/netcfgx/tcpipconf_notify.c#L4257-L4402) 让设置立即在内核生效,无需重启:
if (pAdapter->NewDhcpEnabled != pAdapter->OldDhcpEnabled)
{
if (pAdapter->NewDhcpEnabled)
{
/* 静态 IP --> DHCP:删除当前静态 IP */
DeleteIPAddress(pAdapter->OldIp->NTEContext);
}
else
{
/* DHCP --> 静态 IP:通知 DHCP 客户端 */
DhcpCApiInitialize(&DhcpApiVersion);
DhcpStaticRefreshParams(pAdapter->Index,
htonl(pAdapter->NewIp->IpAddress),
htonl(pAdapter->NewIp->u.SubnetMask));
DhcpCApiCleanup();
}
}
else if (IpAddressesChanged(pAdapter))
{
/* 静态 IP --> 静态 IP */
DeleteIPAddress(pAdapter->OldIp->NTEContext);
AddIPAddress(htonl(pAdapter->NewIp->IpAddress),
htonl(pAdapter->NewIp->u.SubnetMask),
pAdapter->Index,
&pAdapter->NewIp->NTEContext, &NTEInstance);
}
同时处理:
- 默认路由:枚举
GetIpForwardTable()删除该网卡目的地址为 0 的旧路由 →CreateIpForwardEntry()添加新网关路由 - dnscache 通知:DNS 变化时
OpenService(L"Dnscache")+ControlService(SERVICE_CONTROL_PARAMCHANGE)通知 DNS 缓存服务重读配置
6.8 各对话框的窗口过程
| 对话框函数 | 对应资源 | 功能 |
|---|---|---|
TcpipBasicDlg | IDD_TCPIP_BASIC_DLG | 常规页:DHCP/静态单选、IP/掩码/网关、DNS 服务器、高级按钮 |
TcpipAltConfDlg | IDD_TCPIP_ALTCF_DLG | 备用配置页:APIPA / 用户配置 |
TcpipAdvancedIpDlg | IDD_TCPIP_ADVIP_DLG | 高级→IP 设置:多 IP/掩码、多网关/跃点数、接口跃点数 |
TcpipAdvancedDnsDlg | IDD_TCPIP_ADVDNS_DLG | 高级→DNS:DNS 地址列表、DNS 后缀搜索列表、动态注册 |
TcpipAdvancedOptDlg | IDD_TCPIP_ADVOPT_DLG | 高级→选项:TCP/IP 筛选(打开筛选对话框) |
TcpipFilterSettingsDlg | IDD_TCPIP_FILTER_DLG | TCP/IP 筛选:TCP/UDP 端口、IP 协议允许/限制列表 |
TcpipAddIpDlg / TcpipAdvGwDlg / TcpipAddDNSDlg / TcpipAddSuffixDlg / TcpipFilterPortDlg | IDD_TCPIPADDIP_DLG 等 | 添加/修改单条 IP、网关、DNS、后缀、端口的小对话框 |
IP 地址输入控件:使用 Windows 标准 SysIPAddress32(IP 地址控件),配合 IPM_SETRANGE(限制每段 0~255)与 IPM_GETADDRESS 校验。输入 IP 后自动按 A/B/C 类地址推导子网掩码(255.0.0.0 / 255.255.0.0 / 255.255.255.0)。
IP 校验细节(TcpipAddIpDlg 的 WM_NOTIFY):
if (dwIpAddr <= MAKEIPADDRESS(127, 255, 255, 255))
SendDlgItemMessageW(hwndDlg, IDC_SUBNETMASK, IPM_SETADDRESS, 0, MAKEIPADDRESS(255, 0, 0, 0));
else if (dwIpAddr <= MAKEIPADDRESS(191, 255, 255, 255))
SendDlgItemMessageW(hwndDlg, IDC_SUBNETMASK, IPM_SETADDRESS, 0, MAKEIPADDRESS(255, 255, 0, 0));
else if (dwIpAddr <= MAKEIPADDRESS(223, 255, 255, 255))
SendDlgItemMessageW(hwndDlg, IDC_SUBNETMASK, IPM_SETADDRESS, 0, MAKEIPADDRESS(255, 255, 255, 0));
7. 属性页对话框资源
资源定义见 [resource.h](file:///d:/reactos/dll/win32/netcfgx/resource.h),对话框模板见 [lang/zh-CN.rc](file:///d:/reactos/dll/win32/netcfgx/lang/zh-CN.rc)。
7.1 常规页(IDD_TCPIP_BASIC_DLG)控件布局
┌──────────────────────────────────────────┐
│ 常规 │
│ 如果您的网络支持自动分配 IP 地址... │
│ (•) 自动取得 IP 地址 [自动取得IP] │
│ ( ) 使用下列的 IP 地址(U): │
│ IP 地址: [IPADDR SysIPAddress32] │
│ 子网掩码: [SUBNETMASK ] │
│ 网关: [DEFGATEWAY ] │
│ (•) 自动获得 DNS 服务器地址 │
│ ( ) 使用以下的 DNS 服务器地址(U) │
│ 首选的 DNS 服务器:[DNS1 ] │
│ DNS 服务器: [DNS2 ] │
│ [高级(A)] │
└──────────────────────────────────────────┘
关键控件 ID:IDC_USEDHCP(1000) / IDC_NODHCP(1001) / IDC_IPADDR(1002) / IDC_SUBNETMASK(1003) / IDC_DEFGATEWAY(1004) / IDC_AUTODNS(1005) / IDC_FIXEDDNS(1006) / IDC_DNS1(1007) / IDC_DNS2(1008) / IDC_ADVANCED(1009)。
交互逻辑(TcpipBasicDlg 的 WM_COMMAND):
- 选中"自动取得 IP 地址"(
IDC_USEDHCP)→ 清空 IP/掩码/网关,禁用这三个控件,并通过PSM_INSERTPAGE动态插入"备用配置"页([AddAlternativeDialog](file:///d:/reactos/dll/win32/netcfgx/tcpipconf_notify.c#L2742-L2754)) - 选中"使用下列的 IP 地址"(
IDC_NODHCP)→ 启用 IP/掩码/网关,强制 DNS 为"使用以下 DNS",并通过PSM_REMOVEPAGE移除备用配置页 - "高级"按钮 →
LaunchAdvancedTcpipSettings()弹出 3 页高级属性表
7.2 其他对话框
IDD_TCPIP_ALTCF_DLG(备用配置):APIPA 自动专用 IP / 用户配置(IP、掩码、网关、首选/备用 DNS)IDD_TCPIP_ADVIP_DLG(高级→IP 设置):IDC_IPLIST(IP+掩码列表)、IDC_GWLIST(网关+跃点数列表)、接口跃点数IDC_IFMETRICIDD_TCPIP_ADVDNS_DLG(高级→DNS):DNS 服务器列表、主 DNS 后缀 / 选择后缀搜索列表、DNS 注册选项IDD_TCPIP_ADVOPT_DLG+IDD_TCPIP_FILTER_DLG(TCP/IP 筛选):TCP 端口、UDP 端口、IP 协议 允许/限制IDD_TCPIPADDIP_DLG/IDD_TCPIPGW_DLG/IDD_TCPIPDNS_DLG/IDD_TCPIPSUFFIX_DLG/IDD_TCPIP_PORT_DLG:单项添加/修改小对话框
7.3 字符串资源(zh-CN)
IDS_TCPIP "ReactOS-TCP/IP" (对话框标题)
IDS_IPADDR "IP 地址"
IDS_SUBMASK "子网掩码"
IDS_GATEWAY "默认网关"
IDS_METRIC "跃点数"
IDS_AUTOMATIC "自动" (网关自动跃点数)
IDS_DHCPACTIVE "DHCP 已启用" (高级 IP 页中的占位)
IDS_NO_IPADDR_SET "请输入 IP 地址。"
IDS_NO_SUBMASK_SET "请输入子网掩码。"
IDS_DUP_IPADDR "此 IP 地址已存在。"
IDS_DUP_GW "此网关已存在。"
IDS_METRIC_RANGE "跃点数必须在 1 到 9999 之间。"
IDS_DISABLE_FILTER "禁用 TCP/IP 筛选将允许您的计算机接受所有端口上的通信。"
8. 注册表数据存储
TCP/IP 设置集中存储在(与 Windows 一致):
HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters
├── UseDomainNameDevolution REG_DWORD 全局:域名后缀代化
├── SearchList REG_SZ 全局:DNS 后缀搜索列表
├── EnableSecurityFilters REG_DWORD 全局:TCP/IP 筛选开关
└── Interfaces\{AdapterGuid} 每个网卡一个子键
├── EnableDHCP REG_DWORD 0=静态, 1=DHCP
├── IPAddress REG_MULTI_SZ 静态 IP 列表(DHCP 时为 "0.0.0.0")
├── SubnetMask REG_MULTI_SZ 静态子网掩码列表
├── DefaultGateway REG_MULTI_SZ 网关列表
├── DefaultGatewayMetric REG_MULTI_SZ 网关跃点数
├── NameServer REG_SZ DNS 服务器(逗号分隔)
├── InterfaceMetric REG_DWORD 接口跃点数
├── Domain REG_SZ DNS 后缀
├── RegisterAdapterName REG_DWORD DNS 注册
├── RegistrationEnabled REG_DWORD
├── TCPAllowedPorts REG_MULTI_SZ 筛选:允许的 TCP 端口
├── UDPAllowedPorts REG_MULTI_SZ
├── RawIPAllowedProtocols REG_MULTI_SZ
└── ActiveConfigurations 备用配置激活标记
(备用配置实际存于 DHCP\Configurations\Alternate_{GUID}\Options, REG_BINARY)
HKLM\SYSTEM\CurrentControlSet\Services\DHCP\Configurations\Alternate_{GUID}
└── Options REG_BINARY (AlternateConfiguration 结构:IP/掩码/网关/DNS1/DNS2)
tcpip.sys 协议栈启动时读取 Tcpip\Parameters 来配置接口(详见 [TCPIP_驱动分析.md](file:///d:/reactos/doc/TCP/TCPIP_驱动分析.md));DHCP 客户端服务 dhcpcsvc 同样读写该路径(见 [dhclient.c](file:///d:/reactos/base/services/dhcpcsvc/dhcp/dhclient.c) 的 setup_adapter)。
9. 应用变更的运行时路径(到内核 tcpip.sys)
ApplyPnpChanges 调用 IPHLPAPI([iphlpapi_main.c](file:///d:/reactos/dll/win32/iphlpapi/iphlpapi_main.c)):
INetCfgComponentControl_fnApplyPnpChanges (netcfgx/tcpipconf_notify.c)
├─ DeleteIPAddress(NTEContext) (IPHLPAPI)
│ └─ TDI/IOCTL → tcpip.sys 删除 IP 地址
├─ AddIPAddress(IP, Mask, IfIndex, ...) (IPHLPAPI)
│ └─ addIPAddress() → TDI \Device\Tcp → tcpip.sys 添加 IP 地址
├─ DhcpStaticRefreshParams(...) (dhcpcapi → dhcpcsvc)
├─ GetIpForwardTable / DeleteIpForwardEntry 删除旧默认路由
├─ CreateIpForwardEntry(网关路由) (IPHLPAPI → tcpip.sys 路由表)
└─ OpenService(L"Dnscache") + ControlService(SERVICE_CONTROL_PARAMCHANGE)
AddIPAddress([iphlpapi_main.c#L112](file:///d:/reactos/dll/win32/iphlpapi/iphlpapi_main.c#L112-L115))内部通过 TDI 客户端接口(NtCreateFile打开\Device\Tcp+ DeviceIoControl/IOCTL_TCP_SET_INFORMATION_EX等)通知 tcpip.sys 变更地址与路由。- tcpip.sys 侧对应的接口地址管理(
IP_INTERFACE.Unicast/Netmask等)参见 [tcpip网络接口层分析.md](file:///d:/reactos/doc/TCP/tcpip网络接口层分析.md) 与 [tcpip网络层IP分析.md](file:///d:/reactos/doc/TCP/tcpip网络层IP分析.md)。 - DHCP 模式则由 dhcpcsvc 服务维护租约并动态写入地址,界面上的"备用配置"只在 DHCP 不可用时生效(APIPA 169.254.x.x 或用户备用静态配置)。
10. 完整调用链图
[控制面板] ncpa.cpl
│ ShellExecuteW("explorer.exe", 网络连接 CLSID 路径)
▼
[外壳] netshell.dll CNetworkConnections (IShellFolder 文件夹视图)
│ 右键连接 → IContextMenu::InvokeCommand("属性")
▼
[外壳] ShowNetConnectionProperties(pCon, hwnd)
│ 创建 CNetConnectionPropertyUi → AddPages() → PropertySheetW
▼
[外壳] LANPropertiesUIDlg (IDD_NETPROPERTIES 连接属性属性表)
│ 组件列表列出: 客户端/服务/协议 (EnumComponents)
│ 选中 "Internet 协议 (TCP/IP)" → 属性按钮
▼
[外壳] ShowNetworkComponentProperties(hwndDlg)
│ pNCfgComp->RaisePropertyUi(NCRP_SHOW_PROPERTY_UI)
▼
[netcfgx] INetCfgComponent::RaisePropertyUi (inetcfgcomp_iface.c)
│ QueryInterface(INetCfgComponentPropertyUi) → MergePropPages
▼
[netcfgx] TcpipConfNotifyImpl (tcpipconf_notify.c)
│ SetContext: 用 INetLanConnectionUiInfo::GetDeviceGuid 定位当前网卡
│ MergePropPages: 创建 IDD_TCPIP_BASIC_DLG (+ IDD_TCPIP_ALTCF_DLG 若 DHCP)
▼
[TCP/IP 属性对话框] TcpipBasicDlg
│ 用户填写 IP/掩码/网关/DNS → 确定 (PSN_APPLY → StoreTcpipBasicSettings)
│ 高级 → 3 页高级属性表 (IP设置/DNS/选项)
▼
[netcfgx] INetCfg::Apply (netcfg_iface.c)
├─ ApplyRegistryChanges → RegSetValueExW → Tcpip\Parameters\Interfaces\{GUID}
└─ ApplyPnpChanges → DeleteIPAddress / AddIPAddress / CreateIpForwardEntry
→ IPHLPAPI → TDI/IOCTL → tcpip.sys (内核生效)
└─ ControlService(dnscache) 刷新 DNS 缓存
11. 关键文件索引
| 文件 | 说明 |
|---|---|
| [ncpa.c](file:///d:/reactos/dll/cpl/ncpa/ncpa.c) | 控制面板"网络连接"入口,打开外壳文件夹 |
| [shfldr_netconnect.cpp](file:///d:/reactos/dll/shellext/netshell/shfldr_netconnect.cpp) | 网络连接文件夹视图 + 右键菜单("属性"动词) |
| [lanconnectui.cpp](file:///d:/reactos/dll/shellext/netshell/lanconnectui.cpp) | 连接属性对话框;组件列表;RaisePropertyUi 触发点;INetLanConnectionUiInfo |
| [lanconnectui.h](file:///d:/reactos/dll/shellext/netshell/lanconnectui.h) | 连接属性 UI 头文件 |
| [setup.cpp](file:///d:/reactos/dll/shellext/netshell/setup.cpp) | 网络组件安装向导(使用 INetCfg) |
| [netcfgx.c](file:///d:/reactos/dll/win32/netcfgx/netcfgx.c) | netcfgx 入口;CLSID_CNetCfg / CLSID_TcpipConfigNotifyObject 类工厂 |
| [netcfg_iface.c](file:///d:/reactos/dll/win32/netcfgx/netcfg_iface.c) | INetCfg 主接口;Apply 分发 |
| [inetcfgcomp_iface.c](file:///d:/reactos/dll/win32/netcfgx/inetcfgcomp_iface.c) | INetCfgComponent(RaisePropertyUi 等) |
| [installer.c](file:///d:/reactos/dll/win32/netcfgx/installer.c) | NetClassInstaller 设备类安装器 |
| [tcpipconf_notify.c](file:///d:/reactos/dll/win32/netcfgx/tcpipconf_notify.c) | 核心:TCP/IP 属性对话框、注册表读写、PnP 应用 |
| [propertypage.c](file:///d:/reactos/dll/win32/netcfgx/propertypage.c) | 网卡设备 Ndi\Params 高级属性页 |
| [resource.h](file:///d:/reactos/dll/win32/netcfgx/resource.h) | 对话框/控件/字符串 ID 定义 |
| [lang/zh-CN.rc](file:///d:/reactos/dll/win32/netcfgx/lang/zh-CN.rc) | 中文对话框模板与字符串资源 |
| [iphlpapi_main.c](file:///d:/reactos/dll/win32/iphlpapi/iphlpapi_main.c) | AddIPAddress/DeleteIPAddress 等运行时应用 API |
| [dhclient.c](file:///d:/reactos/base/services/dhcpcsvc/dhcp/dhclient.c) | DHCP 客户端:租约获取与注册表写入 |
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/caimouse/article/details/163609863



