PowerShell Pipeline

Checking for a Locked File Using PowerShell

We've all been there: We're using code to automate something and send to a specific file, or attempting to relocate a file from one folder to another. We find the file and think it's available to open and read/write. Then we go to move or open it, and bam -- we find out the file is already opened and in use, meaning that our attempts to do anything with it are gone until whatever process currently using the file has released its lock on it.

Sometimes it ends in an error message or pop-up that can be annoying to deal with. OK, not really "annoying to deal with," but still something that would be nice to avoid. If only there was a simple way of checking to see if that item was already being used.

(I do want to note that if you use Notepad to open up a file, it will not actually lock the file even though you can clearly see it being used. For some reason, Notepad will open the file and keep it in memory and then only claim a lock on it long enough to save the updates.)

Believe it or not, the process to test for this is rather simple and, at best, can be accomplished in a little as three lines of code.

PS C:\> $FileStream = [System.IO.File]::Open("C:\users\proxb\desktop\Checking for Locked Files.docx",'Open','Write')
Exception calling "Open" with "3" argument(s): "The process cannot access the file 
'C:\Checking for Locked Files.docx' because it is being used by another process."
At line:1 char:1
+ $FileStream = [System.IO.File]::Open("C:\Checking ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : IOException

So what does this code mean? Great question! The first line of code is really the meat of the work to determine if a file is open or not. I am calling the Open method from the type of System.Io.File. Withing that method, it is looking for three parameters to supply before it can process the data.

PS C:\> [System.IO.File]::Open

static System.IO.FileStream Open(string path, System.IO.FileMode mode)                                                             
static System.IO.FileStream Open(string path, System.IO.FileMode mode, System.IO.FileAccess access)                                
static System.IO.FileStream Open(string path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share)

For my testing, I am using the parameter set that includes the string path, the System.IO.Filemode mode and System.IO.FileAccess access, which will give me just enough access to test whether the file is open or not.

From there, if I am able to get access to the file -- which tells me that the file is not locked -- I then clean up after myself by calling Dispose and Close methods of the filestream object that has been created.

PS C:\> $FileStream = [System.IO.File]::Open("C:\SomeScript.ps1",'Open','Write')

PS C:\> $FileStream

CanRead        : False
CanWrite       : True
CanSeek        : True
IsAsync        : False
Length         : 6851
Name           : C:\SomeScript.ps1
Position       : 0
Handle         : 3748
SafeFileHandle : Microsoft.Win32.SafeHandles.SafeFileHandle
CanTimeout     : False
ReadTimeout    : 
WriteTimeout   :  

Make sure that you Close and Dispose of the stream. Otherwise, you will have created a lock on the file and prevent access to it by another user or application.

With a lot of things that I may write ad hoc initially, I like to go back and make them into a full-fledged function for easier use with other code. With my function found below called Test-IsFileLocked, I make use of a Try/Catch to handle the errors, which first checks to make sure I'm not denied access to the file. If that isn't the case, then it is assumed that the file is locked.

PS C:\> Test-IsFileLocked -Path 'C:\Checking for Locked Files.docx'

File                                                  IsLocked
----                                                  --------
C:\Checking for Locked Files.docx     True 

Source Code

Function Test-IsFileLocked {
    Param (
    Process {
        ForEach ($Item in $Path) {
            #Ensure this is a full path
            $Item = Convert-Path $Item
            #Verify that this is a file and not a directory
            If ([System.IO.File]::Exists($Item)) {
                Try {
                    $FileStream = [System.IO.File]::Open($Item,'Open','Write')
                    $IsLocked = $False
                } Catch [System.UnauthorizedAccessException] {
                    $IsLocked = 'AccessDenied'
                } Catch {
                    $IsLocked = $True
                    File = $Item
                    IsLocked = $IsLocked
Now you know how to check for a file (as long as it isn't opened in Notepad) to determine if it is locked by some other user or application.

About the Author

Boe Prox is a Microsoft MVP in Windows PowerShell and a Senior Windows System Administrator. He has worked in the IT field since 2003, and he supports a variety of different platforms. He is a contributing author in PowerShell Deep Dives with chapters about WSUS and TCP communication. He is a moderator on the Hey, Scripting Guy! forum, and he has been a judge for the Scripting Games. He has presented talks on the topics of WSUS and PowerShell as well as runspaces to PowerShell user groups. He is an Honorary Scripting Guy, and he has submitted a number of posts as a to Microsoft's Hey, Scripting Guy! He also has a number of open source projects available on Codeplex and GitHub. His personal blog is at http://learn-powershell.net.

comments powered by Disqus
Most   Popular