彻底清理 Windows 11 「打开方式」中已卸载软件的残留记录

在 Windows 11 上卸载 Cursor、Kiro、Qoder、Antigravity 等编辑器之后,右键文件时「打开方式」菜单里仍然会显示这些已经不存在的程序。这是因为卸载程序没有完整清理注册表,导致文件关联记录残留。本文记录完整的排查和清理过程。


残留位置分析

这类残留分布在注册表的以下几个位置:

位置 说明
HKCU:\Software\Classes\Applications\程序名.exe 程序的应用注册键
HKCU:\Software\Classes\.扩展名\OpenWithProgids 各文件类型的「打开方式」关联值
HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.扩展名\OpenWithList 资源管理器记录的历史打开列表
HKCU:\Software\Classes\Local Settings\Software\Microsoft\Windows\Shell\MuiCache 程序显示名称缓存

第一步:全面扫描残留

管理员身份打开 PowerShell,运行以下扫描脚本,确认残留位置:

$keywords = @("Antigravity", "Cursor", "Kiro", "Qoder")

$searchPaths = @(
    "HKCU:\Software\Classes",
    "HKCU:\Software\Classes\Applications",
    "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts",
    "HKLM:\SOFTWARE\Classes\Applications",
    "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths"
)

$found = $false
foreach ($kw in $keywords) {
    foreach ($path in $searchPaths) {
        if (Test-Path $path) {
            Get-ChildItem $path -Recurse -ErrorAction SilentlyContinue |
            Where-Object { $_.PSChildName -like "*$kw*" } |
            ForEach-Object {
                Write-Host "[$kw] 键名残留: $($_.Name)"
                $found = $true
            }
            Get-ChildItem $path -Recurse -ErrorAction SilentlyContinue |
            ForEach-Object {
                $regPath = $_.PSPath
                try {
                    $props = Get-ItemProperty -Path $regPath -ErrorAction Stop
                    $props.PSObject.Properties |
                    Where-Object { $_.Name -notmatch "^PS" -and $_.Value -like "*$kw*" } |
                    ForEach-Object {
                        Write-Host "[$kw] 值残留: 路径=$regPath | 名=$($_.Name) | 值=$($_.Value)"
                        $found = $true
                    }
                } catch {}
            }
        }
    }
}

if (-not $found) { Write-Host "✅ 未发现任何残留,已清理干净!" }

第二步:一键清理所有残留

确认有残留后,运行以下清理脚本:

$keywords = @("Antigravity", "Cursor", "Kiro", "Qoder")

# 1. 删除 Applications 注册键
foreach ($kw in $keywords) {
    $appKey = "HKCU:\Software\Classes\Applications\$kw.exe"
    if (Test-Path $appKey) {
        Remove-Item $appKey -Recurse -Force
        Write-Host "已删除 Applications 键: $kw.exe"
    }
}

# 2. 清理各扩展名 OpenWithProgids 里的残留值
$classesPath = "HKCU:\Software\Classes"
Get-ChildItem $classesPath -ErrorAction SilentlyContinue | ForEach-Object {
    $progidsPath = "$($_.PSPath)\OpenWithProgids"
    if (Test-Path $progidsPath) {
        $props = Get-ItemProperty $progidsPath -ErrorAction SilentlyContinue
        if ($props) {
            foreach ($kw in $keywords) {
                $props.PSObject.Properties | Where-Object {
                    $_.Name -like "*$kw*" -and $_.Name -notmatch "^PS"
                } | ForEach-Object {
                    Remove-ItemProperty -Path $progidsPath -Name $_.Name -Force -ErrorAction SilentlyContinue
                    Write-Host "已清理 OpenWithProgids: $($_.Name)"
                }
            }
        }
    }
}

# 3. 清理 FileExts 下 OpenWithList 里的残留值
$fileExtsPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts"
Get-ChildItem $fileExtsPath -ErrorAction SilentlyContinue | ForEach-Object {
    $owlPath = "$($_.PSPath)\OpenWithList"
    if (Test-Path $owlPath) {
        $props = Get-ItemProperty $owlPath -ErrorAction SilentlyContinue
        if ($props) {
            foreach ($kw in $keywords) {
                $props.PSObject.Properties | Where-Object {
                    $_.Value -like "*$kw*" -and $_.Name -notmatch "^PS" -and $_.Name -ne "MRUList"
                } | ForEach-Object {
                    Remove-ItemProperty -Path $owlPath -Name $_.Name -Force -ErrorAction SilentlyContinue
                    Write-Host "已清理 FileExts OpenWithList: $($_.Name) = $($_.Value)"
                }
            }
        }
    }
}

# 4. 清理 MuiCache 里的残留
$muiPath = "HKCU:\Software\Classes\Local Settings\Software\Microsoft\Windows\Shell\MuiCache"
if (Test-Path $muiPath) {
    $props = Get-ItemProperty $muiPath -ErrorAction SilentlyContinue
    foreach ($kw in $keywords) {
        $props.PSObject.Properties | Where-Object {
            $_.Name -like "*$kw*" -and $_.Name -notmatch "^PS"
        } | ForEach-Object {
            Remove-ItemProperty -Path $muiPath -Name $_.Name -Force -ErrorAction SilentlyContinue
            Write-Host "已清理 MuiCache: $($_.Name)"
        }
    }
}

Write-Host "全部清理完成!请重启资源管理器。"

第三步:验证清理结果

清理完成后,再次运行第一步的扫描脚本,若输出如下则说明清理彻底:

✅ 未发现任何残留,已清理干净!

第四步:重启资源管理器

Ctrl + Shift + Esc 打开任务管理器,找到「Windows 资源管理器」,右键选择「重新启动」,右键菜单即刻生效。


注意事项

⚠️ 操作注册表前建议先备份:打开注册表编辑器(regedit)→ 文件 → 导出,保存为 .reg 文件。

⚠️ 脚本中的 $keywords 数组可以按需修改,只清理你实际卸载的程序。

⚠️ 如果你仍在使用某款软件(如 Cursor),将其从 $keywords 中移除,避免误删。

⚠️ 清理注册表残留不会影响这些软件的重新安装,安装程序会重新写入所有必要记录。


总结

Windows 在卸载程序时经常无法完整清理注册表中的文件关联记录,导致「打开方式」菜单越来越杂乱。通过 PowerShell 脚本可以精准定位并批量清理这些残留,整个过程安全可逆,不影响系统正常运行及后续重新安装软件。