问题现象
一台长期使用、经历过多次系统迁移、网卡变化或连接环境变化的 Windows 电脑,可能会出现这样的网络名称:
EXAMPLE_WIFI
EXAMPLE_WIFI 2
EXAMPLE_WIFI 3
或者:
网络
网络 3
网络 4
网络 5
……
甚至几十条。
这些记录并不一定意味着系统中保存了几十个 Wi-Fi 密码。
真正的 Wi-Fi 配置和 Windows 的 NetworkList 网络历史记录,是两个不同的系统。
这一区别非常重要。
一、Windows 实际上保存了两类网络信息
WLAN Profile
Wi-Fi SSID、密码、加密方式、自动连接设置等主要由 WLAN Profile 保存。
查看:
netsh wlan show profiles
输出类似:
All User Profile : EXAMPLE_WIFI
这些配置包含真正的 Wi-Fi 连接参数。
删除:
netsh wlan delete profile name="EXAMPLE_WIFI"
会导致保存的 Wi-Fi 配置一起消失。
如果目标只是整理“网络 2、网络 3”之类的历史名称,通常不应该执行这个命令。
NetworkList
Windows 还维护另一套网络识别数据库:
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkList
其中最重要的两个区域是:
Profiles
Signatures
Profiles 负责保存 Windows 曾经识别到的网络身份。
Signatures 则负责保存识别这些网络时使用的一些特征,例如:
- 网关 MAC;
- 网络描述;
- DNS 信息;
- FirstNetwork;
- 网络签名。
于是,即使 Wi-Fi Profile 只有一个:
EXAMPLE_WIFI
NetworkList 中仍可能出现:
EXAMPLE_WIFI
EXAMPLE_WIFI 2
EXAMPLE_WIFI 3
二、为什么 Windows 会自动增加数字后缀
Windows 会根据网络签名判断“这是不是以前那个网络”。
如果某些识别条件发生变化,例如:
- 路由器更换;
- 网关 MAC 改变;
- 网卡更换;
- 驱动重新安装;
- Windows 系统迁移;
- 网络共享方式改变;
- USB 网卡或虚拟网卡变化;
- DHCP / 网络拓扑变化;
系统就可能认为这是一个“新的网络”。
如果名称已经存在,就自动生成:
EXAMPLE_WIFI 2
EXAMPLE_WIFI 3
因此,NetworkList 本质上是一套网络历史数据库。
使用时间越长,里面越容易积累大量旧记录。
三、读取全部 NetworkList Profiles
管理员 PowerShell:
$profileRoot = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkList\Profiles'
Get-ChildItem $profileRoot |
ForEach-Object {
$p = Get-ItemProperty $_.PSPath
[PSCustomObject]@{
ProfileName = $p.ProfileName
Description = $p.Description
Category = $p.Category
GUID = $_.PSChildName
}
} |
Sort-Object ProfileName |
Format-Table -AutoSize
其中:
Category = 0
通常代表 Public。
Category = 1
通常代表 Private。
四、首先确认当前正在使用的网络
在删除任何记录之前,都应该读取当前活动连接:
Get-NetConnectionProfile |
Format-Table Name, InterfaceAlias, InterfaceIndex, NetworkCategory -AutoSize
例如:
Name InterfaceAlias NetworkCategory
------------ ---------------- ---------------
EXAMPLE_WIFI Wi-Fi Private
当前活动 Profile 应当受到保护。
五、自动找出带数字后缀的重复组
可以将名称末尾的数字去掉,然后分组:
$profiles = @(
Get-ChildItem $profileRoot |
ForEach-Object {
$p = Get-ItemProperty $_.PSPath
[PSCustomObject]@{
BaseName = $p.ProfileName -replace '\s+\d+$',''
ProfileName = $p.ProfileName
Description = $p.Description
Category = $p.Category
GUID = $_.PSChildName
}
}
)
$profiles |
Group-Object BaseName |
Where-Object {
$_.Count -gt 1
} |
ForEach-Object {
Write-Host "`n--- $($_.Name) ---"
$_.Group |
Sort-Object ProfileName |
Format-Table ProfileName, Description, Category, GUID -AutoSize
}
可能得到:
EXAMPLE_WIFI
EXAMPLE_WIFI 2
EXAMPLE_WIFI 3
这种情况就是明显的数字后缀组。
六、不要把所有“以数字结尾”的名称都当成重复项
这是非常重要的一点。
例如:
OFFICE-5G
PHONE-2026
DEVICE 1318
数字本身可能就是网络名称的一部分。
因此,不应该简单使用:
ProfileName -match '\d+$'
然后全部删除。
更安全的方法是:
- 去除数字后缀;
- 确认同一个 BaseName 至少有两条记录;
- 确认其中存在无数字版本;
- 只删除该组中的数字版本。
例如:
$duplicateGroups = @(
$profiles |
Group-Object BaseName |
Where-Object {
$_.Count -gt 1 -and
($_.Group.ProfileName -contains $_.Name)
}
)
这样,单独存在的数字名称不会被误删。
七、清理前备份整个 NetworkList
建议先完整导出:
$backup = "$env:USERPROFILE\Desktop\NetworkList-Backup"
New-Item -ItemType Directory -Path $backup -Force |
Out-Null
reg.exe export `
"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkList" `
"$backup\NetworkList.reg" /y
这样即使误操作,也保留了恢复依据。
八、删除数字后缀 Profile
生成删除目标:
$targets = @(
$duplicateGroups |
ForEach-Object {
$_.Group |
Where-Object {
$_.ProfileName -match '\s+\d+$'
}
}
)
删除前应检查当前活动网络是否进入目标列表:
$activeNames = @(
Get-NetConnectionProfile |
Select-Object -ExpandProperty Name
)
$activeTargets = @(
$targets |
Where-Object {
$_.ProfileName -in $activeNames
}
)
if ($activeTargets.Count -gt 0) {
throw 'Active NetworkList profile is in deletion list.'
}
如果安全检查通过,再处理。
九、为什么还必须清理 Signatures
只删除:
NetworkList\Profiles
并不代表这条网络历史完全消失。
它对应的网络签名可能仍然保留在:
NetworkList\Signatures\Managed
NetworkList\Signatures\Unmanaged
读取方式:
$sigRoots = @(
'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkList\Signatures\Managed',
'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkList\Signatures\Unmanaged'
)
每个 Signature 都带有:
ProfileGuid
因此,可以根据 GUID 找出对应关系。
删除 Profile 时,最好同时删除指向该 GUID 的 Signature。
十、清理孤立 Signature
有些系统在历史操作后还会遗留这样的情况:
Signature → 某个 ProfileGuid
但:
Profiles\{该 GUID}
已经不存在。
这就是孤立 Signature。
检测:
$profileGuids = @(
Get-ChildItem $profileRoot |
Select-Object -ExpandProperty PSChildName
)
$orphans = @()
foreach ($root in $sigRoots) {
if (!(Test-Path $root)) {
continue
}
foreach ($key in Get-ChildItem $root) {
$s = Get-ItemProperty $key.PSPath -ErrorAction SilentlyContinue
if (
$s.ProfileGuid -and
$s.ProfileGuid -notin $profileGuids
) {
$orphans += [PSCustomObject]@{
ProfileGuid = $s.ProfileGuid
Description = $s.Description
Path = $key.PSPath
}
}
}
}
确认后即可删除:
foreach ($orphan in $orphans) {
Remove-Item $orphan.Path -Recurse -Force
}
十一、最终验证
检查数字重复组
$after = @(
Get-ChildItem $profileRoot |
ForEach-Object {
$p = Get-ItemProperty $_.PSPath
[PSCustomObject]@{
BaseName = $p.ProfileName -replace '\s+\d+$',''
ProfileName = $p.ProfileName
GUID = $_.PSChildName
}
}
)
$after |
Group-Object BaseName |
Where-Object {
$_.Count -gt 1 -and
($_.Group.ProfileName -contains $_.Name)
}
理想情况下不应再得到目标重复组。
检查孤立 Signature
再次比较:
Signatures.ProfileGuid
和:
Profiles.GUID
理想结果应为:
No orphaned NetworkList signatures remain.
检查当前活动网络
Get-NetConnectionProfile |
Format-Table Name, InterfaceAlias, InterfaceIndex, NetworkCategory -AutoSize
当前网络名称应保持正常。
十二、这项操作不会删除什么
如果整个过程中没有调用:
netsh wlan delete profile
那么 NetworkList 清理通常不会删除:
- Wi-Fi SSID;
- Wi-Fi 密码;
- WPA/WPA2/WPA3 配置;
- 自动连接设置;
- WLAN Profile。
也就是说:
NetworkList 历史记录
与:
保存的 Wi-Fi Profile
是两回事。
这也是清理此类问题时最需要区分的概念。
总结
Windows 中出现:
网络
网络 2
网络 3
或者:
EXAMPLE_WIFI
EXAMPLE_WIFI 2
EXAMPLE_WIFI 3
通常不是 Wi-Fi 配置本身出了问题,而是 NetworkList 长期积累了多个网络身份记录。
比较完整的清理流程是:
读取活动网络
↓
读取全部 NetworkList Profiles
↓
找出数字后缀重复组
↓
备份 NetworkList
↓
保护当前活动 Profile
↓
删除数字后缀 Profiles
↓
删除对应 Signatures
↓
检查并清理 orphaned Signatures
↓
重新验证
核心原则可以概括成一句话:
整理 NetworkList,不等于删除 WLAN Profile。
只要严格限定操作范围,就可以清除多年积累的“网络 2、网络 3”历史记录,同时保留真正需要的 Wi-Fi 配置和密码。