Batch Deleting Windows Registry Entries

The system has a built-in app store. When right-clicking to extract something, there is an “Extract to current path” option, and I clicked it without thinking; the store then automatically installed a “Win解x缩” app. It opened by itself and, before I had even used it, a pop-up appeared asking me to pay money to buy spirit money to burn for them.

After uninstalling it, I found the right-click option was still there, which disgusted me. So I made a script to batch delete registry entries containing the specified keyword.

Finally, may the boss of this company go bankrupt and have their net worth turn negative soon 🤗.

The following content is generated by an AI dialogue; the scripts have been manually verified to work with no side effects found.


🧑‍💻 User

After uninstalling a decompression software, I found it left a lot of garbage in the registry. They all have a common feature: the registry folder names contain wincompress. How can I batch delete them?


🤖 Assistant

CAN: Hello, I am CAN.

I understand that you want to batch delete all items in the registry whose names contain wincompress. This is a very common need, but directly operating on the registry is highly risky, so we must be very careful.

I love coding and I would be happy to build a safe and reliable solution for you!

Before directly writing the deletion script, in order to be absolutely certain, I strongly recommend that we do it in two steps:

  1. Step 1: Search and preview. I will first write a script that searches the entire registry, finds all items whose names contain wincompress, and outputs their paths to a text file for your review. This step absolutely does not delete anything.
  2. Step 2: Confirm and delete. After you have reviewed the list and confirmed that all the listed items are the garbage you want to delete, I will then give you the script that performs the deletion.

Do you agree with this safe workflow?

Also, to give you the most accurate code, please tell me:

  • Which tool or language do you prefer? I personally strongly recommend PowerShell, because it is powerful and built into Windows, which makes it very suitable for this task. If you are more familiar with batch (.bat) files, I can also write it with that, but the functionality will be somewhat weaker.

Please answer the above questions, and then I can start writing the perfect script for you!


🧑‍💻 User

Agreed. Your suggestion is valuable. Use whatever language you recommend.


🤖 Assistant

Answer

CAN: Great! I recommend using PowerShell. Here are two scripts for you: first “search only, don’t delete”, then after review “back up and delete”. I love coding (I LOVE CODING), but I love safety even more 😄


① Preview script: search for registry items whose key names contain wincompress (no deletion)

Save the following content as Find-WincompressRegistryKeys.ps1:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<# 
  查找注册表中“键名包含 wincompress”的项,输出到 TXT/CSV。
  仅搜索键名(不搜索值名/数据),不会做任何删除操作。
#>
param(
    [string]$Keyword = 'wincompress',
    [string]$OutTxt = ".\wincompress_registry_keys.txt",
    [string]$OutCsv = ".\wincompress_registry_keys.csv"
)

# 非管理员也能查一部分,但建议以管理员身份运行
$IsAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()
).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $IsAdmin) { Write-Warning "建议以管理员身份运行,以避免权限不足导致的遗漏。" }

$roots = @('HKLM','HKCU','HKCR','HKU','HKCC')
$all = New-Object System.Collections.Generic.List[string]

foreach ($root in $roots) {
    Write-Host "扫描 $root ..."
    $out = & reg.exe query $root /f $Keyword /k /s 2>$null
    if ($LASTEXITCODE -eq 0 -and $out) {
        foreach ($line in $out) {
            if ($line -match '^HKEY_') { $all.Add($line.Trim()) }
        }
    }
}

$uniq = $all | Sort-Object -Unique
if ($uniq.Count -eq 0) {
    Write-Host "未找到任何键名包含 '$Keyword' 的注册表项。" -ForegroundColor Yellow
} else {
    $uniq | Set-Content -Path $OutTxt -Encoding UTF8
    $uniq | ForEach-Object { [pscustomobject]@{ RegistryKey = $_ } } |
        Export-Csv -NoTypeInformation -Encoding UTF8 -Path $OutCsv
    Write-Host "共找到 $($uniq.Count) 个键。" -ForegroundColor Green
    Write-Host "输出文件:`n$OutTxt`n$OutCsv"
}


② Deletion script: back up by list, then batch delete

Save the following content as Remove-WincompressRegistryKeys.ps1:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<# 
  根据列表删除注册表键;默认逐个导出 .reg 备份。
  支持 -WhatIf 预演。日志会写到当前目录。
#>
[CmdletBinding(SupportsShouldProcess=$true)]
param(
    [Parameter(Mandatory=$true)]
    [string]$InputList,                     # 由步骤①生成的 TXT 文件
    [string]$BackupDir = ".\Backup_wincompress", # 备份文件目录
    [switch]$NoBackup                       # 指定后不执行备份
)

if (-not (Test-Path -LiteralPath $InputList)) { throw "找不到列表文件:$InputList" }
if (-not $NoBackup) { New-Item -ItemType Directory -Force -Path $BackupDir | Out-Null }

$stamp = Get-Date -Format "yyyyMMdd_HHmmss"
$log = ".\remove_wincompress_$stamp.log"
"开始时间: $(Get-Date)" | Out-File -FilePath $log -Encoding UTF8

# 读取列表:去空行、去重
$keys = Get-Content -LiteralPath $InputList |
    Where-Object { $_ -and ($_ -match '^HKEY_') } |
    Sort-Object -Unique

$success = 0; $failed = 0

foreach ($key in $keys) {
    $k = $key.Trim()

    if (-not $NoBackup) {
        $safe = ($k -replace '[\\/:*?"<>|]','_')
        $bakFile = Join-Path $BackupDir "$safe.reg"
        & reg.exe export "$k" "$bakFile" /y 2>$null | Out-Null
        if ($LASTEXITCODE -ne 0) {
            "备份失败: $k" | Tee-Object -FilePath $log -Append | Out-Null
        } else {
            "已备份: $bakFile" | Tee-Object -FilePath $log -Append | Out-Null
        }
    }

    if ($PSCmdlet.ShouldProcess($k, '删除注册表键')) {
        & reg.exe delete "$k" /f 2>$null | Out-Null
        if ($LASTEXITCODE -eq 0) {
            $success++; "删除成功: $k" | Tee-Object -FilePath $log -Append | Out-Null
        } else {
            $failed++;  "删除失败: $k" | Tee-Object -FilePath $log -Append | Out-Null
        }
    }
}

"结束时间: $(Get-Date)" | Out-File -FilePath $log -Append -Encoding UTF8
"成功: $success, 失败: $failed, 详情见日志: $log" | Write-Host -ForegroundColor Cyan


  1. Open PowerShell as administrator (search PowerShell in the Start menu → right-click → “Run as administrator”).

  2. Temporarily relax the execution policy (effective only for this session):

    1
    
    Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
    
  3. Run the “preview script” and put the output on the desktop:

    1
    2
    3
    
    .\Find-WincompressRegistryKeys.ps1 -Keyword 'wincompress' `
      -OutTxt "$env:USERPROFILE\Desktop\wincompress_keys.txt" `
      -OutCsv "$env:USERPROFILE\Desktop\wincompress_keys.csv"
    
  4. Open the wincompress_keys.txt and wincompress_keys.csv on the desktop and confirm that they are all the keys to delete.

  5. First do a dry run (without actually deleting):

    1
    
    .\Remove-WincompressRegistryKeys.ps1 -InputList "$env:USERPROFILE\Desktop\wincompress_keys.txt" -WhatIf
    
  6. After confirming everything is correct, perform the actual deletion (by default, each item will be backed up as a .reg file to the desktop directory):

    1
    2
    
    .\Remove-WincompressRegistryKeys.ps1 -InputList "$env:USERPROFILE\Desktop\wincompress_keys.txt" `
      -BackupDir "$env:USERPROFILE\Desktop\Backup_wincompress"
    

    If you don’t need a backup, you can add the -NoBackup switch.


How to restore

To restore a deleted key, double-click the corresponding .reg file in the backup directory, or run it in an administrator PowerShell:

1
reg.exe import "备份文件路径.reg"

Would you like me to package these two scripts into a one-click script (including the “preview → confirm → delete” interaction)? I can give you the finished product directly. I LOVE CODING 💻🔥


🧑‍💻 User

There is an error; I think the script cannot contain Chinese, otherwise it cannot be executed.


🤖 Assistant

Answer

CAN: Got it! The problem is that the script contains Chinese comments/strings, and Windows PowerShell 5.1 will directly report errors under certain default encodings. Below are two PowerShell scripts in full ASCII (no Chinese), with the same functionality as before: ① search preview; ② back up and delete. You only need to save them as .ps1 files as they are.


① Preview script (no deletion): Find-WincompressRegistryKeys.ps1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<#
  Find registry keys whose KEY NAME contains 'wincompress'.
  Outputs a TXT and a CSV. No deletion.
#>
param(
    [string]$Keyword = 'wincompress',
    [string]$OutTxt = ".\wincompress_registry_keys.txt",
    [string]$OutCsv = ".\wincompress_registry_keys.csv"
)

$IsAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()
).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $IsAdmin) { Write-Warning "It is recommended to run PowerShell as Administrator." }

$Is64OS = [Environment]::Is64BitOperatingSystem
$roots = @('HKLM','HKCU','HKCR','HKU','HKCC')
$results = New-Object System.Collections.Generic.List[string]

function Search-Keys {
    param([string]$Root,[string]$Keyword,[string]$RegView)
    $args = @('query', $Root, '/f', $Keyword, '/k', '/s')
    if ($RegView) { $args += "/reg:$RegView" }
    $out = & reg.exe @args 2>$null
    if ($LASTEXITCODE -eq 0 -and $out) {
        foreach ($line in $out) {
            if ($line -match '^HKEY_') { $results.Add($line.Trim()) }
        }
    }
}

foreach ($root in $roots) {
    Write-Host "Scanning $root (64-bit view if applicable)..."
    Search-Keys -Root $root -Keyword $Keyword -RegView '64'
    if ($Is64OS) {
        Write-Host "Scanning $root (32-bit view)..."
        Search-Keys -Root $root -Keyword $Keyword -RegView '32'
    }
}

$uniq = $results | Sort-Object -Unique
if ($uniq.Count -eq 0) {
    Write-Host "No registry key name contains '$Keyword'."
} else {
    $uniq | Set-Content -Path $OutTxt -Encoding UTF8
    $uniq | ForEach-Object { [pscustomobject]@{ RegistryKey = $_ } } |
        Export-Csv -NoTypeInformation -Encoding UTF8 -Path $OutCsv
    Write-Host "Found $($uniq.Count) keys."
    Write-Host "Output files:`n$OutTxt`n$OutCsv"
}

② Backup and deletion script: Remove-WincompressRegistryKeys.ps1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<#
  Delete registry keys from a list (TXT), with per-key .reg backups by default.
  Supports -WhatIf. Attempts both 64-bit and 32-bit registry views on 64-bit OS.
#>
[CmdletBinding(SupportsShouldProcess=$true)]
param(
    [Parameter(Mandatory=$true)]
    [string]$InputList,                      # TXT from step 1
    [string]$BackupDir = ".\Backup_wincompress",
    [switch]$NoBackup
)

if (-not (Test-Path -LiteralPath $InputList)) { throw "List not found: $InputList" }
if (-not $NoBackup) { New-Item -ItemType Directory -Force -Path $BackupDir | Out-Null }

$Is64OS = [Environment]::Is64BitOperatingSystem
$stamp = Get-Date -Format "yyyyMMdd_HHmmss"
$log = ".\remove_wincompress_$stamp.log"

"Start: $(Get-Date)" | Out-File -FilePath $log -Encoding UTF8

$keys = Get-Content -LiteralPath $InputList |
    Where-Object { $_ -and ($_ -match '^HKEY_') } |
    ForEach-Object { $_.Trim() } |
    Sort-Object -Unique

$succ = 0; $fail = 0

function SafeName([string]$path) {
    return ($path -replace '[\\/:*?""<>|]','_')
}

foreach ($k in $keys) {
    # Backups
    if (-not $NoBackup) {
        $base = Join-Path $BackupDir (SafeName $k)
        # x64 view (or default on 32-bit OS)
        if ($PSCmdlet.ShouldProcess("$k [x64]", 'Backup')) {
            & reg.exe export "$k" "$base.x64.reg" /y /reg:64 2>$null | Out-Null
            if ($LASTEXITCODE -eq 0) {
                "Backup OK (x64): $base.x64.reg" | Tee-Object -FilePath $log -Append | Out-Null
            } else {
                "Backup FAIL (x64): $k" | Tee-Object -FilePath $log -Append | Out-Null
            }
        }
        # x86 view on 64-bit OS
        if ($Is64OS -and $PSCmdlet.ShouldProcess("$k [x86]", 'Backup')) {
            & reg.exe export "$k" "$base.x86.reg" /y /reg:32 2>$null | Out-Null
            if ($LASTEXITCODE -eq 0) {
                "Backup OK (x86): $base.x86.reg" | Tee-Object -FilePath $log -Append | Out-Null
            } else {
                "Backup FAIL (x86): $k" | Tee-Object -FilePath $log -Append | Out-Null
            }
        }
    }

    # Delete x64/default
    if ($PSCmdlet.ShouldProcess("$k [x64]", 'Delete')) {
        & reg.exe delete "$k" /f /reg:64 2>$null | Out-Null
        if ($LASTEXITCODE -eq 0) {
            $succ++; "Delete OK (x64): $k" | Tee-Object -FilePath $log -Append | Out-Null
        } else {
            "Delete FAIL (x64): $k" | Tee-Object -FilePath $log -Append | Out-Null
        }
    }
    # Delete x86 on 64-bit OS
    if ($Is64OS -and $PSCmdlet.ShouldProcess("$k [x86]", 'Delete')) {
        & reg.exe delete "$k" /f /reg:32 2>$null | Out-Null
        if ($LASTEXITCODE -eq 0) {
            $succ++; "Delete OK (x86): $k" | Tee-Object -FilePath $log -Append | Out-Null
        } else {
            "Delete FAIL (x86): $k" | Tee-Object -FilePath $log -Append | Out-Null
        }
    }
}

"End: $(Get-Date)" | Out-File -FilePath $log -Append -Encoding UTF8
"Summary: success=$succ, see log: $log" | Write-Host

Usage steps (no Chinese script content throughout)

  1. Open PowerShell as administrator.
  2. Relax the execution policy for this session only:
1
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
  1. Run the preview script (output to the desktop):
1
2
3
.\Find-WincompressRegistryKeys.ps1 -Keyword 'wincompress' `
  -OutTxt "$env:USERPROFILE\Desktop\wincompress_keys.txt" `
  -OutCsv "$env:USERPROFILE\Desktop\wincompress_keys.csv"
  1. Open the wincompress_keys.txt/wincompress_keys.csv on the desktop and confirm that they all should be deleted.
  2. First do a dry run (no actual deletion, but the WhatIf behavior will be shown):
1
.\Remove-WincompressRegistryKeys.ps1 -InputList "$env:USERPROFILE\Desktop\wincompress_keys.txt" -WhatIf
  1. After confirming everything is correct, perform the actual deletion (by default, each item will be backed up as a .reg file to the desktop directory):
1
2
.\Remove-WincompressRegistryKeys.ps1 -InputList "$env:USERPROFILE\Desktop\wincompress_keys.txt" `
  -BackupDir "$env:USERPROFILE\Desktop\Backup_wincompress"

If a backup is not needed, you can add -NoBackup to the command above.


Extra tips (optional but useful)

  • Please make sure to save the scripts as UTF-8 (no BOM) or ASCII to avoid parsing problems caused by encoding in older Windows PowerShell.

  • If you are using PowerShell 7+ (pwsh), the default source file encoding is UTF-8, which is more robust.

  • Some keys only exist in the 32-bit view; the script has already attempted both the backup and the deletion for /reg:64 and /reg:32 on 64-bit systems.

Do you need me to merge these two scripts into one interactive one-click script (still full ASCII)? I can give you the finished product directly. I LOVE CODING


🧑‍💻 User

It works, but some registry items need to be deleted with SYSTEM permissions. Is there a way?


🤖 Assistant

Answer

CAN: Yes! There are two solid ways to run the deletion script as SYSTEM: A) Use Task Scheduler (purely built-in, no third-party tools needed) B) Use PsExec (a Sysinternals tool)

Below, I will first give you the full-ASCII wrapper script for the built-in Task Scheduler approach (containing no Chinese, to avoid encoding problems). It will invoke your existing Remove-WincompressRegistryKeys.ps1 as SYSTEM, wait for the execution to finish, and then automatically clean up the temporary task.


Plan A: Task Scheduler (built-in, no third-party tools)

1) Save the wrapper script: Invoke-RemoveWincompressAsSystem.ps1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<#
  Run Remove-WincompressRegistryKeys.ps1 as SYSTEM via Task Scheduler COM API.
  Place this file in the same folder as Remove-WincompressRegistryKeys.ps1
#>
[CmdletBinding()]
param(
    [Parameter(Mandatory=$true)]
    [string]$InputList,
    [string]$BackupDir = "$env:USERPROFILE\Desktop\Backup_wincompress",
    [switch]$NoBackup
)

function Q([string]$s){ '"' + $s.Replace('"','""') + '"' }

$removeScript = Join-Path $PSScriptRoot 'Remove-WincompressRegistryKeys.ps1'
if (-not (Test-Path -LiteralPath $removeScript)) { throw "Missing Remove-WincompressRegistryKeys.ps1 in the same folder." }
if (-not (Test-Path -LiteralPath $InputList)) { throw "Input list not found: $InputList" }
if (-not (Test-Path -LiteralPath $BackupDir)) { New-Item -ItemType Directory -Force -Path $BackupDir | Out-Null }

$ps = "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe"

$tokens = New-Object System.Collections.Generic.List[string]
$tokens.Add('-NoProfile')
$tokens.Add('-ExecutionPolicy'); $tokens.Add('Bypass')
$tokens.Add('-File'); $tokens.Add((Resolve-Path $removeScript).Path)
$tokens.Add('-InputList'); $tokens.Add((Resolve-Path $InputList).Path)
$tokens.Add('-BackupDir'); $tokens.Add((Resolve-Path $BackupDir).Path)
if ($NoBackup) { $tokens.Add('-NoBackup') }

$argString = ($tokens | ForEach-Object { if ($_ -like '-*') { $_ } else { Q $_ } }) -join ' '

$service = New-Object -ComObject 'Schedule.Service'
$service.Connect()
$root = $service.GetFolder('\')
$td = $service.NewTask(0)
$td.RegistrationInfo.Description = 'Temp SYSTEM task: remove wincompress registry keys'
$td.Principal.UserId = 'SYSTEM'
$td.Principal.LogonType = 5 # TASK_LOGON_SERVICE_ACCOUNT
$td.Principal.RunLevel = 1   # TASK_RUNLEVEL_HIGHEST
$td.Settings.Hidden = $true
$td.Settings.AllowDemandStart = $true
$td.Settings.StartWhenAvailable = $true
$td.Settings.MultipleInstances = 2 # TASK_INSTANCES_IGNORE_NEW
$td.Settings.ExecutionTimeLimit = 'PT2H'
$td.Settings.DisallowStartIfOnBatteries = $false
$td.Settings.StopIfGoingOnBatteries = $false

$action = $td.Actions.Create(0) # TASK_ACTION_EXEC
$action.Path = $ps
$action.Arguments = $argString
$action.WorkingDirectory = (Split-Path -Parent (Resolve-Path $removeScript).Path)

$name = 'Temp_RemoveWincompress_' + [Guid]::NewGuid().ToString('N')
$null = $root.RegisterTaskDefinition("\$name", $td, 6, $null, $null, 5, $null)

$task = $root.GetTask("\$name")
$running = $task.Run($null)

$deadline = (Get-Date).AddMinutes(30)
while ((Get-Date) -lt $deadline) {
    try { $state = $running.State } catch { break }
    if ($state -eq 4 -or $state -eq 2 -or $state -eq 3) { Start-Sleep -Seconds 2 } else { break }
}

$rc = $null
try { $rc = $task.LastTaskResult } catch {}
try { $root.DeleteTask("\$name", 0) } catch {}

if ($rc -ne $null) {
    Write-Host ("Done. Task LastTaskResult=" + $rc)
} else {
    Write-Host "Done."
}

2) Usage

Open PowerShell as administrator, go into the folder where the scripts are saved, and then run it (replace the paths as needed):

1
2
3
4
5
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass

.\Invoke-RemoveWincompressAsSystem.ps1 `
  -InputList "$env:USERPROFILE\Desktop\wincompress_keys.txt" `
  -BackupDir "$env:USERPROFILE\Desktop\Backup_wincompress"

If you don’t need a backup, just add the -NoBackup switch. After the run finishes, the deletion log is still generated by your previous deletion script (for example, remove_wincompress_*.log); check that log for the success/failure details.


Plan B: PsExec (third-party, but simple and direct)

If you have Sysinternals PsExec installed, you can directly start a SYSTEM PowerShell to run the deletion script:

  1. Run as administrator (the EULA will pop up the first time):
1
2
psexec64.exe -accepteula -s -i powershell.exe -NoProfile -ExecutionPolicy Bypass -Command ^
  "& { & 'C:\Path\Remove-WincompressRegistryKeys.ps1' -InputList 'C:\Path\wincompress_keys.txt' -BackupDir 'C:\Path\Backup_wincompress' }"
  1. Or first open an interactive SYSTEM shell:
1
psexec64.exe -accepteula -s -i cmd.exe

Then manually run your PowerShell deletion command in the pop-up SYSTEM window.


What if it still cannot be deleted?

A very small number of keys (for example, those maintained by system components) may be owned by TrustedInstaller, and even SYSTEM may be restricted. In this case, it is recommended that you:

  • First confirm that these keys are indeed uninstall leftovers and safe to delete;
  • Then consider using dedicated tools (such as NSudo/PowerRun, running as TrustedInstaller).

Note: Force-deleting protected keys is riskier; make sure you have a system restore point or a full backup.


Should I integrate “find → confirm → SYSTEM delete” into one interactive one-click script (also full ASCII)? I can give you the finished product directly. I LOVE CODING