caimouse头像
关注

ReactOS 网络 IP 地址设置系统配置窗口分析

ReactOS 网络 IP 地址设置系统配置窗口分析

目录

  1. 概述
  2. 总体架构与调用链
  3. 入口:ncpa.cpl 网络连接控制面板
  4. netshell.dll 网络连接外壳扩展
  5. netcfgx.dll 网络配置核心
  6. TCP/IP 属性对话框实现(tcpipconf_notify.c)
  7. 属性页对话框资源
  8. 注册表数据存储
  9. 应用变更的运行时路径(到内核 tcpip.sys)
  10. 完整调用链图
  11. 关键文件索引

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/...)

三个关键衔接点:

衔接点内容
外壳 → COMnetshell 通过 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:文件夹视图对象(IShellFolderIShellView),枚举系统中的网络连接。
  • 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 实现 INetConnectionPropertyUiINetLanConnectionUiInfo

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.cppCoCreateInstance(CLSID_CNetCfg) 获取 INetCfg,加写锁 → InitializeQueryNetCfgClassInstallApply,用于网络组件的安装向导。
  • 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_CNetCfgINetCfg_Constructor核心网络配置对象(INetCfg 体系)
CLSID_TcpipConfigNotifyObjectTcpipConfigNotify_ConstructorTCP/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)INetCfgComponentRaisePropertyUi(触发属性页)、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_fnApplyRegistryChangesINetCfgComponentControl_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 接口

接口关键函数作用
INetCfgComponentControlInitialize / ApplyRegistryChanges / ApplyPnpChanges / CancelChanges初始化、写注册表、运行时应用、取消
INetCfgComponentPropertyUiSetContext / MergePropPages / ValidateProperties / ApplyProperties属性页创建与上下文绑定
INetCfgComponentSetupInstall / Upgrade / ReadAnswerFile / Removing安装/升级/应答文件/移除(当前均为空实现)
ITcpipPropertiesITcpipProperties_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,然后遍历 pAdapterListHeadAdapterName(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) 按差异写注册表:

注册表值类型写入条件
InterfaceMetricREG_DWORDMetric 变化
RegisterAdapterName / RegistrationEnabledREG_DWORD变化时
DomainREG_SZ总是
EnableDHCPREG_DWORDDHCP 开关变化或 IP 变化
IPAddress / SubnetMaskREG_MULTI_SZ静态模式;DHCP 模式写 "0.0.0.0"
DefaultGateway / DefaultGatewayMetricREG_MULTI_SZ网关变化
NameServerREG_SZDNS 变化(空则删除)
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 各对话框的窗口过程

对话框函数对应资源功能
TcpipBasicDlgIDD_TCPIP_BASIC_DLG常规页:DHCP/静态单选、IP/掩码/网关、DNS 服务器、高级按钮
TcpipAltConfDlgIDD_TCPIP_ALTCF_DLG备用配置页:APIPA / 用户配置
TcpipAdvancedIpDlgIDD_TCPIP_ADVIP_DLG高级→IP 设置:多 IP/掩码、多网关/跃点数、接口跃点数
TcpipAdvancedDnsDlgIDD_TCPIP_ADVDNS_DLG高级→DNS:DNS 地址列表、DNS 后缀搜索列表、动态注册
TcpipAdvancedOptDlgIDD_TCPIP_ADVOPT_DLG高级→选项:TCP/IP 筛选(打开筛选对话框)
TcpipFilterSettingsDlgIDD_TCPIP_FILTER_DLGTCP/IP 筛选:TCP/UDP 端口、IP 协议允许/限制列表
TcpipAddIpDlg / TcpipAdvGwDlg / TcpipAddDNSDlg / TcpipAddSuffixDlg / TcpipFilterPortDlgIDD_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_IFMETRIC
  • IDD_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

文章来源crawl

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--