I was asked on Twitter this morning about a way to find out what process has a lock on a given file. I'm not aware of any PowerShell cmdlet that can do that but I figured there had to be a .NET way and if I could find a code sample I could put something together in PowerShell. After some research I came to the conclusion that programmatic approaches were way above my pay grade but there is still an option I came up with.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
I'm a big believer in the right tool for the job and just because you can do something in PowerShell, doesn't mean you should. The best tool for this job is Handle.exe from the Sysinternals suite which you can download for free. You can specify part of a file name and it will show you the process that has a handle to that file.
But why not have your cake and eat it too? I can take this output and turn it into a PowerShell object. I posted a function recently to convert command line output to objects using regular expressions and named captures. All I need is a regular expression pattern.
PS Scripts:\>[regex]$matchPattern = "(?<Name>\w+\.\w+)\s+pid:\s+(?<PID>\b(\d+)\b)\s+type:\s+(?<Type>\w+)\s+\w+:\s+(?<Path>.*)" PS Scripts:\> g:\sysinternals\handle parent.lock | select -skip 5 | convertfrom-text $matchpattern
Or you can create something specific to this task.
Function Get-LockingProcess { [cmdletbinding()] Param( [Parameter(Position=0,Mandatory=$True, HelpMessage="What is the path or filename? You can enter a partial name without wildcards")] [Alias("name")] [ValidateNotNullorEmpty()] [string]$Path ) #define the path to Handle.exe $Handle = "G:\Sysinternals\handle.exe" [regex]$matchPattern = "(?<Name>\w+\.\w+)\s+pid:\s+(?<PID>\b(\d+)\b)\s+type:\s+(?<Type>\w+)\s+\w+:\s+(?<Path>.*)" $data = &$handle $path $MyMatches = $matchPattern.Matches( $data ) if ($MyMatches.value) { $MyMatches | foreach { [pscustomobject]@{ FullName = $_.groups["Name"].value Name = $_.groups["Name"].value.split(".")[0] ID = $_.groups["PID"].value Type = $_.groups["Type"].value Path = $_.groups["Path"].value } #hashtable } #foreach } #if data else { Write-Warning "No matching handles found" } } #end function
This requires Handle.exe. I've hardcoded the path which you need to adjust or make sure the utility is in your PATH. But now look what I can do!
I know some people get hung up on external dependencies but if it gets the job done I don't see the issue. Especially when the solution is free to begin with!
Enjoy your weekend.
My function assumes you have already run Handle.exe once before so you can accept the EULA. Otherwise, you could modify the function to always accept it.
$data = &$handle /accepteula $path
Had to make a few changes to get this working when multiple processes were returned by handles.exe.
Also added commandline to the process. Not very elegant from a powershell perspective, I am still too c# minded however it works and is what I need. Thanks for your help.
http://pastebin.com/sZ90WP7H