
One of the most frustrating errors an end user or IT administrator can deal with is locked files within Windows.. When you delete a folder, you move a file or edit a setting and you encounter a file locked error message, better deal with it quickly and efficiently.
Microsoft introduced PowerShell as a replacement shell, but it has much more functionality than that and it is a complex and capable language. Let's see in this post how you can use PowerShell to deal with locked files.
The locked file problem
How exactly do you lock a file? Throughout normal use, a procedure creates many resource identifiers as one file. In doing so, processes often lock the file to prevent unwanted configuration changes or other corruption. The problem is often that it is difficult to determine which procedure has locked the file and, later, how to clear that file lock.
Unfortunately, there is no built-in cmdlet to test a file and find out if it is locked or by what procedure. So, you need to create your own functions or package other useful tools out there to help you learn more about these files.
Locked files test
Inside Windows, you can test to see if an individual file is locked. With the following code block, you can test to see if a certain file is locked. the $Item
The variable must be set to a full file path. When testing to see if the file can be opened for writing, how does it look with him [System.IO.File]::Open($Item,'Open','Write')
command, you can tell if the file is locked.
If ([System.IO.File]::Exists($Item)) {
Try {
$FileStream = [System.IO.File]::Open($Item,'Open','Write')
$FileStream.Close()
$FileStream.Dispose()
$IsLocked = $False
} Catch [System.UnauthorizedAccessException] {
$IsLocked = 'AccessDenied'
} Catch {
$IsLocked = $True
}
}
Get-SMBOpenFile
I said Windows doesn't have a built-in function, but there is a case where a function exists. If you have a remote or even administrative share (What c$
), then you can use the Get-SMBOpenFile
cmdlet to report on those open files.
PS C:> Get-SMBOpenFile
FileId SessionId Path ShareRelativePath
------ --------- ---- -----------------
154618822665 154618822657 C:
PS C:>
The downside is that this only works for files that are accessed remotely. No locked files in use on your local system will be reported, so in most cases this is not a viable solution. To close, you can pipe open files returned to the Close-SMBOpenFile
command.
Get-SMBOpenFile | Close-SMBOpenFile
OpenFiles utility
Windows has a built-in utility called openfiles
which can help to list which files are in use and disconnect them. At first sight, looks perfect for your needs! You can even wrap this inside a PowerShell function to make it easier to check and disconnect files.
Open an administrative PowerShell prompt and run the command openfiles /query
. Immediately, you should get an error message stating that the global brand “keep object list” must be activated.
PS C:/> openfiles /query
INFO: The system global flag 'maintain objects list' needs
to be enabled to see local opened files.
See Openfiles /? for more information.
Files opened remotely via local share points:
---------------------------------------------
INFO: No shared open files found.
This list of objects is what truly maintains the list of identifiers that are in use and enables openfiles
to consult that information. To activate this, enter in openfiles /local on
and then restart your computer. The downside to activating this feature is that there is a slight performance impact, that depending on your system, this tool may not be worth using. Having said this, let's see how we can make this work inside PowerShell.
PS C:> openfiles /Query /fo csv /nh
Files opened remotely via local share points:
---------------------------------------------
"ID","Accessed By","Type","Open File (Pathexecutable)"
"608","user","Windows","C:"
PS C:> openfiles /Query /fo csv | Select-Object -Skip 4 | ConvertFrom-CSV
ID Accessed By Type Open File (Pathexecutable)
-- ----------- ---- ---------------------------
608 user Windows C:
PS C:> openfiles /disconnect /id 608
SUCCESS: The connection to the open file "C:" has been terminated.
With the above examples, you can see how to import CSV output from openfiles
and PowerShell. With that information, can disconnect
a file to unlock. Due to the performance impact you may incur when enabling the maintain objects list
capacity, may not be worth it for your needs. That is why, other solutions may be needed.
The application of the handle
Sysinternals is known for the many useful and almost essential IT tools it manufactures. Long time, Microsoft acquired Sysinternals and you can download and use these well-supported tools yourself. Conveniently, there is an application called handles
that provides exactly what you are looking for.
First, you should descargar the application, unzip the files and put the executables in a location that has included your Path environment variable. In doing so, you can easily reference the app where you need it. With a simple query for open files, you can see you get a lot of results (truncated for easy reading).
PS C:/> handle64 -NoBanner
...
------------------------------------------------------------------------------
RuntimeBroker.exe pid: 9860 User
48: File C:WindowsSystem32
188: Section BaseNamedObjects__ComCatalogCache__
1EC: Section BaseNamedObjects__ComCatalogCache__
------------------------------------------------------------------------------
chrome.exe pid: 4628 User
78: File C:Program Files (x86)GoogleChromeApplication78.0.3904.108
1C4: Section Sessions1BaseNamedObjectswindows_shell_global_counters
...
You seem to get what you want, at least one way to find out what files are being used, and you can test them using your locked file code from before. But, How do you make this easier to use? The following code reads each procedure and retrieves only the locked files. The downside is that this takes a while to run as there are many processes.
$Processes = Get-Process
$results = $Processes | Foreach-Object {
$handles = (handle64 -p $_.ID -NoBanner) | Where-Object { $_ -Match " File " } | Foreach-Object {
[PSCustomObject]@{
"Hex" = ((($_ -Split " ").Where({ $_ -NE "" })[0]).Split(":")[0]).Trim()
"File" = (($_ -Split " ")[-1]).Trim()
}
}
If ( $handles ) {
[PSCustomObject]@{
"Name" = $_.Name
"PID" = $_.ID
"Handles" = $handles
}
}
}
Despite this, as a last resort, what you get is an actionable collection of files, listed by procedure, that you know are in use and can be further filtered. If you find that you need to close one of them, you can do the following (as administrator):
PS C:> $results |
>> Where-Object Name -EQ 'Notepad' |
>> Where-Object { $_.Handles.File -Match "test.txt" }
Name PID Handles
---- --- -------
Notepad 12028 {@{Hex=44; File=C:test.txt}
PS C:> handle64 -p 12028 -c 44 -y -nobanner
44: File (R-D) C:test.txt
Handle closed.
You can further wrap all of this in one function to make it even easier to parse and search as needed.. There are many alternatives, especially in combining several methods in a solution that suits your environment.
conclusion
Dealing with locked files can be challenging, especially when you stop what you need to do quickly. There are several alternatives to find and unlock these files, but it takes a bit of work since Windows doesn't have a truly comprehensive built-in method to deal with those locked files. The solutions described should keep the problem brief and allow you to move on to much more important tasks.
No related post