In older versions of Windows, it was impossible to determine what processes had a file locked. The system kept track of the number of locks on a file and processes were responsible for incrementing and decrementing the count to represent their own locks.
Recently (I think starting in Windows XP) Windows included a new Restart Manager to aid in restarting the system during installs. To make restarts easier, Windows now keeps track of the processes that have a file locked, and you can use the Restart Manager to read that list of processes.
The .NET Framework doesn’t have any tools for working with the Restart Manager, so you need to use its API and that requires the usual ugly declarations. Download the example to see all of them in their full glory.
The following code shows the FindLockers method that this example uses to get a list of the processes that have a file locked.
// Return a list of processes that have locks on a file.
static public List
{
// Start a new Restart Manager session.
uint session_handle;
string session_key = Guid.NewGuid().ToString();
int result = RmStartSession(out session_handle, 0, session_key);
if (result != 0)
throw new Exception(“Error ” +
result + ” starting a Restart Manager session.”);
List
try
{
const int ERROR_MORE_DATA = 234;
uint pnProcInfoNeeded = 0, num_procs = 0,
lpdwRebootReasons = RmRebootReasonNone;
string[] resources = new string[] { filename };
result = RmRegisterResources(
session_handle, (uint)resources.Length,
resources, 0, null, 0, null);
if (result != 0)
throw new Exception(“Could not register resource.”);
// There’s a potential race around 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.
result = RmGetList(session_handle, out pnProcInfoNeeded,
ref num_procs, null, ref lpdwRebootReasons);
if (result == ERROR_MORE_DATA)
{
// Create an array to store the process results.
RM_PROCESS_INFO[] processInfo =
new RM_PROCESS_INFO[pnProcInfoNeeded];
num_procs = pnProcInfoNeeded;
// Get the list.
result = RmGetList(session_handle,
out pnProcInfoNeeded, ref num_procs,
processInfo, ref lpdwRebootReasons);
if (result != 0)
throw new Exception(“Error ” + result +
” listing lock processes”);
// Add the results to the list.
for (int i = 0; i < num_procs; i++)
{
try
{
processes.Add(
Process.GetProcessById(processInfo[i].
Process.dwProcessId));
}
// In case the process is no longer running.
catch (ArgumentException) { }
}
}
else if (result != 0)
throw new Exception("Error " + result +
" getting the size of the result.");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
RmEndSession(session_handle);
}
return processes;
}
The method first creates a new Restart Manager session. It then makes an empty list to hold Process objects and enters a try-catch block.
The method then calls the RmRegisterResources method to register the file’s name with the Restart Manager. Next it calls RmGetList method to see how many processes have locks on the file. It calls RmGetList again to get information on the processes. Then code then loops through the results and uses the returned process IDs to get Process objects representing the objects that have the file locked.
The method finishes by returning the list of Process objects.
Download the example to see more details. This example was based on the MSDN post, How to know the process locking a file. See that post for additional information.
To test the program, start it and then open the executable file in Microsoft Word. (It will look like gibberish in Word.) Then press the program’s Find Locks button. You should see processes named howto_find_file_locker.vshost (Visual Studio running the program) and WINWORD.
If you then double-click the executable file, you can run the program again. It and the previously instance of the program should list the two earlier processes plus howto_find_file_locker representing the new instance of the program.
I don’t know of a way to figure out what kind of locks a process might have on a file. For example, if you write a program that uses File.OpenRead to open a file for reading, then that program appears in the list of processes that have the file locked, but I don’t know how to determine whether the lock is for reading or writing. If you figure that out please say something in a comment below.