How to Find Out Which Process Is Locking a File Using .Net

How do I find out which process is locking a file using .NET?

One of the good things about handle.exe is that you can run it as a subprocess and parse the output.

We do this in our deployment script - works like a charm.

Using C#, how does one figure out what process locked a file?

This question had an original answer that is now over 7 years old. That code is preserved at https://gist.github.com/i-e-b/2290426
This old version might work for you if you need to use Windows XP for some reason.

A much better answer is at How to check for file lock?

I've replicated Eric J's answer below (with using statements added, and class & method names to match the old code that was here) Please note that the comments to this answer may be out-of-date.

Research by user 'Walkman' is ongoing to improve the older code, as there are some conditions where the Restart Manager does not list all locks. See Github repo: https://github.com/Walkman100/FileLocks

Use like:

List<Process> locks = Win32Processes.GetProcessesLockingFile(@"C:\Hello.docx");

Code:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace FileLockInfo
{
public static class Win32Processes
{
/// <summary>
/// Find out what process(es) have a lock on the specified file.
/// </summary>
/// <param name="path">Path of the file.</param>
/// <returns>Processes locking the file</returns>
/// <remarks>See also:
/// http://msdn.microsoft.com/en-us/library/windows/desktop/aa373661(v=vs.85).aspx
/// http://wyupdate.googlecode.com/svn-history/r401/trunk/frmFilesInUse.cs (no copyright in code at time of viewing)
/// </remarks>
public static List<Process> GetProcessesLockingFile(string path)
{
uint handle;
string key = Guid.NewGuid().ToString();
int res = RmStartSession(out handle, 0, key);

if (res != 0) throw new Exception("Could not begin restart session. Unable to determine file locker.");

try
{
const int MORE_DATA = 234;
uint pnProcInfoNeeded, pnProcInfo = 0, lpdwRebootReasons = RmRebootReasonNone;

string[] resources = {path}; // Just checking on one resource.

res = RmRegisterResources(handle, (uint) resources.Length, resources, 0, null, 0, null);

if (res != 0) throw new Exception("Could not register resource.");

//Note: there's a race condition here -- the first call to RmGetList() returns
// the total number of process. However, when we call RmGetList() again to get
// the actual processes this number may have increased.
res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons);

if (res == MORE_DATA)
{
return EnumerateProcesses(pnProcInfoNeeded, handle, lpdwRebootReasons);
}
else if (res != 0) throw new Exception("Could not list processes locking resource. Failed to get size of result.");
}
finally
{
RmEndSession(handle);
}

return new List<Process>();
}


[StructLayout(LayoutKind.Sequential)]
public struct RM_UNIQUE_PROCESS
{
public int dwProcessId;
public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime;
}

const int RmRebootReasonNone = 0;
const int CCH_RM_MAX_APP_NAME = 255;
const int CCH_RM_MAX_SVC_NAME = 63;

public enum RM_APP_TYPE
{
RmUnknownApp = 0,
RmMainWindow = 1,
RmOtherWindow = 2,
RmService = 3,
RmExplorer = 4,
RmConsole = 5,
RmCritical = 1000
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct RM_PROCESS_INFO
{
public RM_UNIQUE_PROCESS Process;

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)] public string strAppName;

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)] public string strServiceShortName;

public RM_APP_TYPE ApplicationType;
public uint AppStatus;
public uint TSSessionId;
[MarshalAs(UnmanagedType.Bool)] public bool bRestartable;
}

[DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
static extern int RmRegisterResources(uint pSessionHandle, uint nFiles, string[] rgsFilenames,
uint nApplications, [In] RM_UNIQUE_PROCESS[] rgApplications, uint nServices,
string[] rgsServiceNames);

[DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)]
static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey);

[DllImport("rstrtmgr.dll")]
static extern int RmEndSession(uint pSessionHandle);

[DllImport("rstrtmgr.dll")]
static extern int RmGetList(uint dwSessionHandle, out uint pnProcInfoNeeded,
ref uint pnProcInfo, [In, Out] RM_PROCESS_INFO[] rgAffectedApps,
ref uint lpdwRebootReasons);

private static List<Process> EnumerateProcesses(uint pnProcInfoNeeded, uint handle, uint lpdwRebootReasons)
{
var processes = new List<Process>(10);
// Create an array to store the process results
var processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded];
var pnProcInfo = pnProcInfoNeeded;

// Get the list
var res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons);

if (res != 0) throw new Exception("Could not list processes locking resource.");
for (int i = 0; i < pnProcInfo; i++)
{
try
{
processes.Add(Process.GetProcessById(processInfo[i].Process.dwProcessId));
}
catch (ArgumentException) { } // catch the error -- in case the process is no longer running
}
return processes;
}
}
}

How to check for file lock?

No, unfortunately, and if you think about it, that information would be worthless anyway since the file could become locked the very next second (read: short timespan).

Why specifically do you need to know if the file is locked anyway? Knowing that might give us some other way of giving you good advice.

If your code would look like this:

if not locked then
open and update file

Then between the two lines, another process could easily lock the file, giving you the same problem you were trying to avoid to begin with: exceptions.

How can I programatically determine which application is locking a file?

You could try the code provided in this question over here, or look at other suggestions here.

The general approach is to enumerate the handles of all processes, get the file paths of those handles, and compare against the file you're interested in.

But a problem with this approach is that even if you can determine that the file is locked and which application has the file lock then you will still have to cope with race conditions, for example...

one millisecond later

  • the file is not locked
  • the application that did hold the lock is now not

then two milliseconds later

  • the file is locked (again)
  • a different application has the lock

then three milliseconds later

  • the file is still locked
  • yet another app has the lock

...etc

One suggestion is to attempt to get the file handle in your app, and catch the exception when you can't.

 try
{
using (Stream stream = new FileStream("MyFilename.txt"))
{
}
} catch {
//check here why it failed and ask user to retry if the file is in use.
}

Of course this won't help identify the culprit(s) but at least you have a safer way of attempting to access the file.

How to find out what processes have folder or file locked?

Process Explorer will show you this. Ctrl-F will let you search for a file and list the process(es) that have that file open/locked. You can then close that handle using Process Explorer.

There's also a command line utility called oh.exe available for download. See here for more details

What process locks a file?

You are calling .ToString on a list and that is why you are seeing the output : System.Collections.Generic.List`1[System.Diagnostics.Process].

You need to loop over the returned list from GetProcessesLockingFile and call the ProcessName property of the process object to get the name.

EDIT

ProcessName docs

EXAMPLE

List<Process> processes = Win32Processes.GetProcessesLockingFile("locked_file.dll");

foreach (Process proc in processes)
{
Console.WriteLine("Process: " + proc.ProcessName);
}

File locked by which process?

The short answer to this is no.

However, the long answer is that there are various API calls and WMI methods that you can use to find this information out, but don't expect it to be quick and simple.

If you want to use API calls, take a look at the NtQuerySystemInformation function with the SYSTEM_PROCESS_INFORMATION parameter. This is one of those lovely "undocumented" methods that comes with the wonderful disclaimer:

NtQuerySystemInformation may be
altered or unavailable in future
versions of Windows. Applications
should use the alternate functions
listed in this topic.

So I would suggest avoiding that in favour of using WMI.

You can use the WMI Win32_Process class to enumerate all processes currently running on the machine, and then enumerate all handles each process is holding until you find the file you are looking for. Unfortunatly there is no simple way to go "hey, which process is locking this file", it only works the other way round you have to search down the process list until you find the one that is locking the file you are interested in.

I'd recommend a nice little article on CodeProject titled How To: (Almost) Everything In WMI via C# Part 2: Processes. (Part 1 is also a good read if you like that kind of thing)

Find out who is locking a file on a network share

Just in case someone looking for a solution to this for a Windows based system or NAS:

There is a built-in function in Windows that shows you what files on the local computer are open/locked by remote computer (which has the file open through a file share):

  • Select "Manage Computer" (Open "Computer Management")
  • click "Shared Folders"
  • choose "Open Files"

There you can even close the file forcefully.

Detecting whether a file is locked by another process (or indeed the same process)

There is no need first to check if the file is locked and then access it, as between the check and the access some other process may still get a lock on the file. So, what you do is correct, if you succeed, do your work with the file.



Related Topics



Leave a reply



Submit