Because it is being used by another process: Why There Isn't a No-Dependency, One Line Powershell Solution

While Powershell is incredibly flexible, the openness lends itself to be non-specific in some cases, such as for finding out what is keeping a lock on a file.

Because it is being used by another process: Why There Isn't a No-Dependency, One Line Powershell Solution
Photo by JESHOOTS.COM / Unsplash

The Short

There is no purpose built module to get the handle. You have to either import a non-default module or write C#.

The Long

I wanted to write a post around a simple one line Powershell script to work out which process has a lock on a file - even if it had to be ugly and in-lined. Something easy I could copy from this post and use it at work, home or elsewhere. Turns out, it isn't so easy. But if you do want to use Powershell, here are two ways:

  1. Sysinternals Handle.exe
  2. Importing C# code, such as this.

With the assumption that Powershell and base libraries were enough to cobble together some Windows based solution, I went head first looking at what Win32 APIs needed calling. With longer solutions, we'd hit things like:

Seems fine enough, make a couple of calls to those Win32 API endpoints and I have my file-locking-process found. Though... It turns out that calling the Win32 APIs requires digging into some .NET in Powershell. Which lead me to the very wrong, "Sure, It'll be a bit more messy, but still easy enough to copy-paste from my blog". Calling Win32 API requires passing C# code as a string to the Add-Type Powershell module. Taking the initial example from Microsoft Development Blog, Use PowerShell to Interact with the Windows API: Part 1 we get the following to copy a file using the CopyFile Win32 API:

$MethodDefinition = @'
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
public static extern bool CopyFile(string lpExistingFileName, string lpNewFileName, bool bFailIfExists);
'@

$Kernel32 = Add-Type -MemberDefinition $MethodDefinition -Name 'Kernel32' -Namespace 'Win32' -PassThru

# You may now call the CopyFile function

# Copy calc.exe to the user’s desktop

$Kernel32::CopyFile("$($Env:SystemRoot)\System32\calc.exe", "$($Env:USERPROFILE)\Desktop\calc.exe", $False) 

It was at this point, that the two options at the start of this post started to look attractive - the problem had already been solved. If I was on a computer I owned, I'd probably want to do this now and again so installing Sysinternals would be useful. Or if I was on a computer I couldn't install it on (but still had admin rights such as a policy controlled work machine) I could save longer .NET based code or throw it into Powershell ISE.

Conclusion

The wheel's already invented, it ain't as pretty as I'd like it, but it spins.