Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

filelock

Windows knows exactly which program has your file open. It just refuses to tell you.

The action can't be completed because the file is open in another program

That dialog is the single least helpful message in Windows. It knows the answer. It will not say it. So you close OBS, you close Premiere, you close Explorer, you give up and reboot.

filelock asks the Restart Manager - the same API Windows Setup uses to work out what it has to close before an update - and prints the answer: the PID, the image name, the account, how long it has been running, and what to do about it.

A single PowerShell script. No install, no dependencies, no driver, no admin needed for your own files. It is read-only: it never closes, kills or restarts anything.


The 10 second version

.\filelock.ps1 "D:\Recordings\stream-2026-03-07.mkv"
filelock 1.0.0   2026-09-13 22:14:58.213
PC\hayyz on PC (elevated)

Target   C:\Users\hayyz\AppData\Local\Temp\fl-demo\clips\stream-2026-03-07.mkv
         size 2.00 GB   modified 2026-09-13 22:14:49.511
Status   HELD by 1 process
         One process has this file open. Details below.

  [1] PID 15576  powershell.exe
      App name        Windows PowerShell
      Type            Console application (RmConsole)
      Running as      PC\hayyz
      Started         2026-09-13 22:14:49.598  (up 8.6 s)
      PID verified    yes - the Restart Manager start time matches the live process
      State           running
      Restartable     no - Windows will not offer to restart it
      Session         1
      Image           C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe
      -> Close the console window running that command.

Summary  1 target, 1 held, 0 free
         1 holding process found in 461.8 ms

That is the whole idea. PID 15576, powershell.exe, started 8.6 seconds ago, and here is what to do.


Why you end up here

  • OBS will not let you move or delete last night's recording.
  • A render fails with "access denied" on a file nothing is visibly using.
  • You cannot eject a drive and Windows will not say which file is busy.
  • Premiere or Resolve keeps a media file locked after you closed the project.
  • Explorer's preview pane is silently holding the clip you clicked on once.
  • A backup or sync tool has a handle open and you cannot work out which one.

You could install Sysinternals Process Explorer, open it, elevate it, learn its Find-Handle dialog and search by substring. filelock is one command and prints a name.


Install

There is nothing to install. Download filelock.ps1 and run it.

git clone https://github.com/appsmypass/filelock.git
cd filelock
.\filelock.ps1 "C:\path\to\your\file.mkv"

If PowerShell blocks the script:

powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\filelock.ps1 "C:\path\to\file.mkv"

That runs it once without changing any machine setting.

Requirements: Windows (any version with rstrtmgr.dll, which is Vista and later) and Windows PowerShell 5.1, which ships in the box. Tested on Windows 11 build 10.0.26200 with PowerShell 5.1.26100.9444.


What it actually tells you

Every holder gets a full record, not just a PID:

Field What it is for
PID The number you need for Task Manager or Stop-Process
App name The friendly name Windows shows, e.g. OBS Studio
Type Main window, console, service, Explorer, or critical system process
Running as The account, so you know if it is yours or SYSTEM
Started Exact start time, to the millisecond
PID verified Whether the PID still belongs to the same process (see below)
Restartable Whether Windows would be willing to close and reopen it
Session Session 0 means a service; session 1 is your desktop
Image Full path to the executable
-> Plain-English advice for that specific kind of holder

The PID verified line

PIDs get recycled. Between the moment the Restart Manager answers and the moment you read the screen, the process can exit and a completely unrelated program can inherit that number. filelock compares the process start time reported by the Restart Manager against the live process and tells you whether they still agree, to within 20 milliseconds. If it says yes, that PID is still the process that holds your file.


Scanning a folder

Point it at a folder to find which file in a batch is the problem:

.\filelock.ps1 D:\Recordings -Recurse -Include *.mkv
filelock 1.0.0   2026-09-13 22:15:00.962
PC\hayyz on PC (elevated)

Target   C:\Users\hayyz\AppData\Local\Temp\fl-demo\clips\stream-2026-03-07.mkv
         size 2.00 GB   modified 2026-09-13 22:14:49.511
Status   HELD by 1 process
         One process has this file open. Details below.

  [1] PID 15576  powershell.exe
      App name        Windows PowerShell
      Type            Console application (RmConsole)
      Running as      PC\hayyz
      Started         2026-09-13 22:14:49.598  (up 11.4 s)
      PID verified    yes - the Restart Manager start time matches the live process
      State           running
      Restartable     no - Windows will not offer to restart it
      Session         1
      Image           C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe
      -> Close the console window running that command.

Summary  2 targets, 1 held, 1 free
         1 holding process found in 412.3 ms

Free files are hidden by default so the held one cannot get lost in the noise. Add -All to list everything:

.\filelock.ps1 D:\Recordings -All
Target   C:\Users\hayyz\AppData\Local\Temp\fl-demo\clips\intro.mp4
         size 18.0 MB   modified 2026-09-13 22:14:49.511
Status   FREE
         Nothing is holding this file. If Windows still refuses, the problem
         is permissions, not a lock.

Target   C:\Users\hayyz\AppData\Local\Temp\fl-demo\clips\raid-night.mkv
         size 700 MB   modified 2026-09-13 22:14:49.511
Status   FREE
         Nothing is holding this file. If Windows still refuses, the problem
         is permissions, not a lock.

Target   C:\Users\hayyz\AppData\Local\Temp\fl-demo\clips\stream-2026-03-07.mkv
         size 2.00 GB   modified 2026-09-13 22:14:49.511
Status   HELD by 1 process
         ...

Summary  3 targets, 1 held, 2 free
         1 holding process found in 437.0 ms

Note the FREE wording. If nothing holds the file and Windows still refuses to let you touch it, you have a permissions problem, not a lock problem - and knowing which of the two you are fighting is most of the battle.


Services and system processes

A service holding a file is a different problem from an app holding one, and filelock gives different advice for each. Here it is on a live registry hive:

.\filelock.ps1 C:\Windows\System32\config\SOFTWARE
Target   C:\Windows\System32\config\SOFTWARE
         size 90.0 MB   modified 2026-09-13 11:16:22.423
Status   HELD by 2 processes
         2 processes have this file open. Details below.

  [1] PID 184    Registry
      App name        Registry
      Type            Critical process (RmCritical)
      Running as      NT AUTHORITY\SYSTEM
      Started         2026-09-13 11:16:40.578  (up 10 h 58 m)
      PID verified    yes - the Restart Manager start time matches the live process
      State           running
      Restartable     no - Windows will not offer to restart it
      Session         0
      Image           (not readable)
      -> This is a critical system process. Do not end it. The file will be
      -> released when Windows is ready.

  [2] PID 4      System
      App name        System
      Type            Critical process (RmCritical)
      Running as      (not readable)
      Started         2026-09-13 11:16:42.880  (up 10 h 58 m)
      PID verified    yes - the Restart Manager start time matches the live process
      State           running
      Restartable     no - Windows will not offer to restart it
      Session         0
      Image           (not readable)
      -> This is a critical system process. Do not end it. The file will be
      -> released when Windows is ready.

  Restart Manager notes
    - a holder could not be inspected with the rights you have
    - a holder belongs to a different session

Summary  1 target, 1 held, 0 free
         2 holding processes found in 493.7 ms

When the holder is a service, the advice names the exact command: net stop <servicename> - because killing a service process is how you get a machine that will not shut down cleanly.

When it is Explorer, the advice reminds you about the preview pane, which is the real culprit far more often than anyone expects.


It never lies about a lock

This is the part that matters, and it is where naive versions of this tool get it wrong. The Restart Manager has several ways of saying "I cannot tell you", and every one of them looks like "nothing is holding this file" if you do not check. filelock reports each one honestly.

Core system DLLs. So many processes map ntdll.dll that the Restart Manager gives up rather than enumerate them:

Target   C:\Windows\System32\ntdll.dll
         size 2.41 MB   modified 2026-09-08 12:14:29.398
Status   TOO MANY
         ERROR_INVALID_HANDLE (6) - the Restart Manager gave up enumerating the holders
         Too many processes map this file for the Restart Manager to
         enumerate.
         That is normal for core system DLLs such as ntdll.dll, which every
         process maps. There is no way to get the list for a file in this
         state.

Summary  1 target, 0 held, 0 free, 1 not queryable
         0 holding processes found in 215.5 ms

A path that is not there.

Target   C:\Users\hayyz\AppData\Local\Temp\fl-demo\clips\nope.mkv
Status   NOT FOUND
         The path does not exist. Check the spelling; a relative path is
         resolved against the current folder.

Summary  1 target, 0 held, 0 free, 1 not queryable
         0 holding processes found in 81.7 ms

A file your account may not read. Unelevated, a protected file returns access denied. filelock says DENIED, never FREE. It also tells DENIED apart from NOT FOUND, because Windows reports an unreadable file and a missing file the same way and the fix is completely different.

A path longer than 260 characters. The Restart Manager rejects it with ERROR_WRITE_FAULT (29), and the \\?\ long-path prefix does not lift that limit. filelock reports PATH TOO LONG with the actual length.

A relative path. This one is a genuine trap in the API: hand the Restart Manager a relative path and it cheerfully returns success with an empty holder list. A tool that does not resolve paths first will tell you a locked file is free. filelock always converts to a full path before it asks, and there is a test that fails if that ever stops happening.


Scripting it

Exit codes

Code Meaning
0 The query ran
1 Bad arguments
2 Nothing could be queried (missing, denied, too long, too many)
3 -FailIfHeld was set and something was held

Gate a script on a clean folder before you start a render or an upload:

.\filelock.ps1 D:\Recordings -Recurse -Quiet -FailIfHeld
if ($LASTEXITCODE -eq 3) { Write-Host "something still has a clip open"; exit 1 }

JSON

-Json prints the whole report as JSON. -Save writes it to a file as UTF-8 without a BOM (not the UTF-16 you get from > file.json in PowerShell 5.1):

.\filelock.ps1 D:\Recordings -Recurse -Save locks.json
saved report to C:\Users\hayyz\AppData\Local\Temp\fl-demo\locks.json

-FromJson replays a saved report, and says so rather than passing a snapshot off as a live reading:

.\filelock.ps1 -FromJson locks.json
filelock 1.0.0   2026-09-13 22:15:13.861
PC\hayyz on PC (elevated)
replayed from C:\Users\hayyz\AppData\Local\Temp\fl-demo\locks.json - a saved snapshot, not a live query

Target   C:\Users\hayyz\AppData\Local\Temp\fl-demo\clips\stream-2026-03-07.mkv
         size 2.00 GB   modified 2026-09-13 22:14:49.511
Status   HELD by 1 process
         One process has this file open. Details below.

  [1] PID 15576  powershell.exe
      ...

Summary  3 targets, 1 held, 2 free
         1 holding process found in 476.1 ms

Aside from that one honest banner line, the replay is character-for-character identical to the original render. There is a test that asserts exactly that.


All options

USAGE
  filelock.ps1 <path> [<path> ...] [options]

OPTIONS
  -Path <p[,p]>    File(s) or folder(s) to inspect. Also accepted positionally.
  -Recurse         Scan sub-folders too when a folder is given.
  -Include <glob>  When a folder is scanned, only files matching this pattern.
  -All             Also list targets that nothing is holding.
  -Top <n>         Stop after n targets.
  -Json            Print the report as JSON instead of text.
  -Save <file>     Write the JSON report to a file (UTF-8, no BOM).
  -FromJson <file> Render a previously saved report instead of querying.
  -Info            Show environment details and exit.
  -Quiet           Suppress the report; useful with -Save or -FailIfHeld.
  -FailIfHeld      Exit with code 3 if any target is held.
  -Version         Print the version and exit.
  -Help            Show this text.

-Info prints what it is running on, which is the first thing to paste into a bug report:

filelock 1.0.0

  Machine        PC
  User           PC\hayyz (elevated)
  OS             Microsoft Windows 11 Pro  build 10.0.26200.0
  PowerShell     5.1.26100.9444
  Architecture   AMD64  64-bit process: True
  rstrtmgr.dll   10.0.26100.1 (WinBuild.160101.0800)
  RM path limit  260 characters
  Working dir    C:\Users\hayyz\.copilot\chats\e24acb27-52a2-46fc-97cb-68dd76caf860

  filelock is read-only. It never closes, kills or restarts anything.

Do I need to run as admin?

For your own files, no. Unelevated, filelock finds holders of your recordings, your documents and your downloads, and it still names Explorer.

For protected files - registry hives, event logs, anything under C:\Windows\System32\config - Windows returns access denied to a standard user. filelock reports DENIED and tells you to re-run elevated. It does not quietly report FREE, which would be worse than useless.


How it works

  1. RmStartSession opens a Restart Manager session.
  2. RmRegisterResources registers the one file, by full path.
  3. RmGetList returns an array of RM_PROCESS_INFO, one per holder.
  4. RmEndSession closes the session.

Then, for each holder, OpenProcess plus QueryFullProcessImageName gets the image path and OpenProcessToken plus GetTokenInformation gets the owning account - both handled gracefully when the process is protected and neither is available.

One session per file, because RmGetList does not attribute holders back to individual files when several are registered at once. A single query takes about 10 milliseconds.

Nothing is written anywhere. RmEndSession leaves no registry key behind - the suite asserts that too.


Testing

Three suites, because "it printed something" is not a test.

selftest.ps1 - 394 hermetic assertions. Every pure function - path resolution, the 260-character limit, the Restart Manager error and bit-flag tables, byte formatting, holder sorting, verdict wording, the JSON round trip, the help text, ASCII hygiene - with no machine state involved. Runs in seconds.

selftest: 394 passed, 0 failed

realcheck.ps1 - 178 assertions against the live machine, each with ground truth this suite constructs itself. It starts a child process, makes it hold a known file, and demands that filelock names that exact PID; it cross-checks the reported start time against Get-Process, the reported service name against the Service Control Manager, and the reported Explorer PID against the running explorer.exe. It checks that a relative path and an absolute path give the same answer, that closing the handle flips held to free, that awkward file names (spaces, brackets, apostrophes, non-ASCII) work, that the target's hash and timestamp are untouched by a query, and it re-runs the tool unelevated via runas /trustlevel:0x20000 to prove a protected file comes back DENIED rather than FREE.

realcheck: 178 passed, 0 failed, 0 skipped

mutate.ps1 - the suites have to be able to fail. This plants 36 deliberate bugs in filelock.ps1 one at a time - the 260 limit becomes 300, access denied is reported as free, the relative path is passed through unresolved, the detected-self bit moves, the plural is inverted - and requires a suite to catch every one. A mutation that survives is a hole in the tests, not a pass. It also runs a behaviour-free comment edit first as a tripwire: if that "fails", the suites are flaky and every kill is suspect. The original is backed up beside itself with a marker file, so an interrupted run is repaired on the next start instead of leaving a deliberately broken tool on disk.

mutate: 36 killed, 0 survived, 0 harness errors

Run them yourself:

.\selftest.ps1
.\realcheck.ps1
.\mutate.ps1

See also

  • obs-4k60-recorder - OBS settings for genuinely smooth 4K60 capture.
  • diskbusy - find out what is hammering your disk while you record.
  • diskscout - find the big files eating your recording drive.
  • dupefind - find duplicate clips by content, not by name.
  • pathfix - find and fix the long and illegal paths that break renders.

Licence

MIT. Copyright (c) 2026 appsmypass. See LICENSE.

About

Names the exact process holding a locked file on Windows, where Explorer only says 'another process'. Uses the Restart Manager API - the same one Windows Setup uses - to report PID, image, service name, start time and restartability. Scans folders, verifies PID identity by start time, JSON output. Zero dependencies, read-only, PowerShell 5.1.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages