guide · static export
PC · troubleshooting

Find Out Which Process Has a File Locked

A file that will not delete, rename or move is held open by something. Resource Monitor, openfiles and handle.exe each answer the question, and Restart Manager answers it for the apps Windows knows about.

by@guidox updated:10/31/26 time:15 min level:Intermediate tested on:Windows 11 24H2 works on:Windows 10, Windows 11, Windows Server 2019 tools:PowerShell as administrator, Sysinternals Handle
TL;DR

Search the handle in **Resource Monitor** first, fall back to **handle.exe -a**, and use the **Restart Manager** API through PowerShell when you need the exact process without installing anything.

Before you start

Requirements

  • The path of the locked file
  • Admin rights for the deeper tools
  • 15 minutes
Step by step

Instructions

01

Reproduce the lock and read the message

The error text names the operation, not the owner. **Remove-Item** and **Move-Item** fail differently, and the exact wording tells you whether a handle exists or the file is read-only.

Remove-Item 'D:\work\report.xlsx' -ErrorAction SilentlyContinue
(Get-Item 'D:\work\report.xlsx').Attributes
Get-Item 'D:\work\report.xlsx' | Select-Object Name,LastWriteTime,Length
02

Use Resource Monitor as the quick check

**resmon** has a search box on the CPU tab that filters every handle by name. It is the fastest visual answer and needs no extra download.

resmon
:: CPU tab > Associated Handles > search box > report.xlsx
Get-Process | Where-Object { $_.Modules.FileName -like '*report*' } | Select-Object Name,Id
03

Ask Windows through Restart Manager

Restart Manager is the component behind the reboot prompt after an install. It can be called from PowerShell and returns the process id and name holding a file.

$sig = '[DllImport("rstrtmgr.dll", CharSet=CharSet.Unicode)] public static extern int RmStartSession(out uint h, int f, string k); [DllImport("rstrtmgr.dll")] public static extern int RmEndSession(uint h); [DllImport("rstrtmgr.dll", CharSet=CharSet.Unicode)] public static extern int RmRegisterResources(uint h, uint n, string[] f, uint a, object b, uint c, object d); [DllImport("rstrtmgr.dll")] public static extern int RmGetList(uint h, out uint n, ref uint na, [In,Out] RM_PROCESS_INFO[] i, ref uint r); public struct RM_PROCESS_INFO { public RM_UNIQUE_PROCESS p; public string s; public string a; public uint t; public uint u; } public struct RM_UNIQUE_PROCESS { public int d; public System.DateTime t; }'
Add-Type -MemberDefinition $sig -Namespace W32 -Name Rm -ErrorAction SilentlyContinue
$key = [guid]::NewGuid().ToString()
$h = 0; [void][W32.Rm]::RmStartSession([ref]$h, 0, $key)
[void][W32.Rm]::RmRegisterResources($h, 1, @('D:\work\report.xlsx'), 0, $null, 0, $null)
$n = 0; $na = 8; $ri = New-Object 'W32.Rm+RM_PROCESS_INFO[]' $na; $r = 0
$rc = [W32.Rm]::RmGetList($h, [ref]$n, [ref]$na, $ri, [ref]$r)
[void][W32.Rm]::RmEndSession($h)
"result $rc, holders $n"; $ri | Where-Object { $_.s } | Select-Object s,a,t
04

Install handle.exe for the hard cases

Sysinternals **handle** walks every handle on the system, which catches services and drivers that the other methods miss. Filtering on the file name keeps the output readable.

handle64.exe -accepteula -nobanner -a "report.xlsx"
handle64.exe -accepteula -nobanner -p explorer | Select-String 'report'
Get-Process explorer | Select-Object Id,StartTime
05

Close the owner without losing work

**Stop-Process** loses unsaved data, so prefer asking the app to close. When the handle holder is a service, restarting the service is gentler than killing the process.

$p = Get-Process -Name excel -ErrorAction SilentlyContinue
$p.CloseMainWindow() | Out-Null
Start-Sleep -Seconds 5
if (-not $p.HasExited) { $p | Stop-Process -Force }
Restart-Service -Name 'Print Spooler' -Force
⚠ warningA file held by the System process usually means a driver or an antivirus scan. Waiting a minute is safer than killing anything named System.
06

Clear the leftover read-only or attribute block

A file that still refuses to move after the owner is gone is often read-only or marked as a system file. Attributes and an open handles check settle which of the two it is.

attrib -r -s -h "D:\work\report.xlsx"
(Get-Item 'D:\work\report.xlsx').IsReadOnly = $false
Remove-Item 'D:\work\report.xlsx' -Force
Test-Path 'D:\work\report.xlsx'
Troubleshooting

Common issues

Nothing seems to hold the file but it still will not delete

Check the attributes and the parent folder permissions. A file marked system in a protected folder fails with a similar message.

handle.exe reports access denied

Run the shell as administrator. Without elevation the tool cannot open handles owned by other sessions.

The holder is a driver or the System process

A filter driver, antivirus or the indexer is scanning the file. Wait, exclude the folder from the scanner, or stop the indexing service temporarily.

The file is on a network share

The lock is on the server, not your PC. Ask on the server with openfiles /query or check the share's open files in Computer Management.

FAQ

Frequently asked

Why does Explorer not say which program holds the file?

The shell only reports the failure. Windows keeps the owner information internally, but no built-in dialog surfaces it; the Restart Manager and handle both read the same data.

Is handle.exe safe to run?

Yes, it is read-only and signed by Microsoft as part of Sysinternals. Close it after the check; it needs no installation.

Do I need to reboot to release the handle?

Almost never. A reboot clears everything, but it is the brute-force option; identifying the holder and closing it takes about a minute.

How to undo this

The guide only reads state and closes the program that held the file. Restart the service or the app you stopped, and set the file attributes back with attrib +r if it was read-only before.

Changelog · 1 entry
  1. 2026-10-31
    First version
Interactive version: checklist, feedback, related guides →

Free · no account needed · tracks your progress