Forcing closed an open file by C# [duplicate] - c#
I've seen several of answers about using Handle or Process Monitor, but I would like to be able to find out in my own code (C#)
which process is locking a file.
I have a nasty feeling that I'm going to have to spelunk around in the win32 API, but if anyone has already done this and can put me on the right track, I'd really appreciate the help.
Update
Links to similar questions
How does one figure out what process locked a file using c#?
Command line tool
Across a Network
Locking a USB device
Unit test fails with locked file
deleting locked file
Long ago it was impossible to reliably get the list of processes locking a file because Windows simply did not track that information. To support the Restart Manager API, that information is now tracked.
I put together code that takes the path of a file and returns a List<Process> of all processes that are locking that file.
using System.Runtime.InteropServices;
using System.Diagnostics;
using System;
using System.Collections.Generic;
static public class FileUtil
{
[StructLayout(LayoutKind.Sequential)]
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;
enum RM_APP_TYPE
{
RmUnknownApp = 0,
RmMainWindow = 1,
RmOtherWindow = 2,
RmService = 3,
RmExplorer = 4,
RmConsole = 5,
RmCritical = 1000
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
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,
UInt32 nFiles,
string[] rgsFilenames,
UInt32 nApplications,
[In] RM_UNIQUE_PROCESS[] rgApplications,
UInt32 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);
/// <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>
static public List<Process> WhoIsLocking(string path)
{
uint handle;
string key = Guid.NewGuid().ToString();
List<Process> processes = new List<Process>();
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 ERROR_MORE_DATA = 234;
uint pnProcInfoNeeded = 0,
pnProcInfo = 0,
lpdwRebootReasons = RmRebootReasonNone;
string[] resources = new string[] { 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 == ERROR_MORE_DATA)
{
// Create an array to store the process results
RM_PROCESS_INFO[] processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded];
pnProcInfo = pnProcInfoNeeded;
// Get the list
res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons);
if (res == 0)
{
processes = new List<Process>((int)pnProcInfo);
// Enumerate all of the results and add them to the
// list to be returned
for (int i = 0; i < pnProcInfo; i++)
{
try
{
processes.Add(Process.GetProcessById(processInfo[i].Process.dwProcessId));
}
// catch the error -- in case the process is no longer running
catch (ArgumentException) { }
}
}
else throw new Exception("Could not list processes locking resource.");
}
else if (res != 0) throw new Exception("Could not list processes locking resource. Failed to get size of result.");
}
finally
{
RmEndSession(handle);
}
return processes;
}
}
Using from Limited Permission (e.g. IIS)
This call accesses the registry. If the process does not have permission to do so, you will get ERROR_WRITE_FAULT, meaning An operation was unable to read or write to the registry. You could selectively grant permission to your restricted account to the necessary part of the registry. It is more secure though to have your limited access process set a flag (e.g. in the database or the file system, or by using an interprocess communication mechanism such as queue or named pipe) and have a second process call the Restart Manager API.
Granting other-than-minimal permissions to the IIS user is a security risk.
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;
}
}
}
It is very complex to invoke Win32 from C#.
You should use the tool Handle.exe.
After that your C# code have to be the following:
string fileName = #"c:\aaa.doc";//Path to locked file
Process tool = new Process();
tool.StartInfo.FileName = "handle.exe";
tool.StartInfo.Arguments = fileName+" /accepteula";
tool.StartInfo.UseShellExecute = false;
tool.StartInfo.RedirectStandardOutput = true;
tool.Start();
tool.WaitForExit();
string outputTool = tool.StandardOutput.ReadToEnd();
string matchPattern = #"(?<=\s+pid:\s+)\b(\d+)\b(?=\s+)";
foreach(Match match in Regex.Matches(outputTool, matchPattern))
{
Process.GetProcessById(int.Parse(match.Value)).Kill();
}
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.
The code I found here,
https://vmccontroller.svn.codeplex.com/svn/VmcController/VmcServices/DetectOpenFiles.cs
Works for me much better than the code provided by Iain. Iain's code seemed to be acquiring a lock of its own. Here is my slightly modified version of the code above modified to return the string path of the files locked instead of the FileSystemInfo object,
using System;
using System.Collections.Generic;
//using System.EnterpriseServices;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using Microsoft.Win32.SafeHandles;
namespace Crmc.Core.BuildTasks
{
using System.Diagnostics;
using System.Linq;
#region ENUMs
internal enum NT_STATUS
{
STATUS_SUCCESS = 0x00000000,
STATUS_BUFFER_OVERFLOW = unchecked((int)0x80000005L),
STATUS_INFO_LENGTH_MISMATCH = unchecked((int)0xC0000004L)
}
internal enum SYSTEM_INFORMATION_CLASS
{
SystemBasicInformation = 0,
SystemPerformanceInformation = 2,
SystemTimeOfDayInformation = 3,
SystemProcessInformation = 5,
SystemProcessorPerformanceInformation = 8,
SystemHandleInformation = 16,
SystemInterruptInformation = 23,
SystemExceptionInformation = 33,
SystemRegistryQuotaInformation = 37,
SystemLookasideInformation = 45
}
internal enum OBJECT_INFORMATION_CLASS
{
ObjectBasicInformation = 0,
ObjectNameInformation = 1,
ObjectTypeInformation = 2,
ObjectAllTypesInformation = 3,
ObjectHandleInformation = 4
}
[Flags]
internal enum ProcessAccessRights
{
PROCESS_DUP_HANDLE = 0x00000040
}
[Flags]
internal enum DuplicateHandleOptions
{
DUPLICATE_CLOSE_SOURCE = 0x1,
DUPLICATE_SAME_ACCESS = 0x2
}
#endregion
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
internal sealed class SafeObjectHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private SafeObjectHandle()
: base(true)
{ }
internal SafeObjectHandle(IntPtr preexistingHandle, bool ownsHandle)
: base(ownsHandle)
{
base.SetHandle(preexistingHandle);
}
protected override bool ReleaseHandle()
{
return NativeMethods.CloseHandle(base.handle);
}
}
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
internal sealed class SafeProcessHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private SafeProcessHandle()
: base(true)
{ }
internal SafeProcessHandle(IntPtr preexistingHandle, bool ownsHandle)
: base(ownsHandle)
{
base.SetHandle(preexistingHandle);
}
protected override bool ReleaseHandle()
{
return NativeMethods.CloseHandle(base.handle);
}
}
#region Native Methods
internal static class NativeMethods
{
[DllImport("ntdll.dll")]
internal static extern NT_STATUS NtQuerySystemInformation(
[In] SYSTEM_INFORMATION_CLASS SystemInformationClass,
[In] IntPtr SystemInformation,
[In] int SystemInformationLength,
[Out] out int ReturnLength);
[DllImport("ntdll.dll")]
internal static extern NT_STATUS NtQueryObject(
[In] IntPtr Handle,
[In] OBJECT_INFORMATION_CLASS ObjectInformationClass,
[In] IntPtr ObjectInformation,
[In] int ObjectInformationLength,
[Out] out int ReturnLength);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern SafeProcessHandle OpenProcess(
[In] ProcessAccessRights dwDesiredAccess,
[In, MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
[In] int dwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool DuplicateHandle(
[In] IntPtr hSourceProcessHandle,
[In] IntPtr hSourceHandle,
[In] IntPtr hTargetProcessHandle,
[Out] out SafeObjectHandle lpTargetHandle,
[In] int dwDesiredAccess,
[In, MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
[In] DuplicateHandleOptions dwOptions);
[DllImport("kernel32.dll")]
internal static extern IntPtr GetCurrentProcess();
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern int GetProcessId(
[In] IntPtr Process);
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool CloseHandle(
[In] IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern int QueryDosDevice(
[In] string lpDeviceName,
[Out] StringBuilder lpTargetPath,
[In] int ucchMax);
}
#endregion
//[ComVisible(true), EventTrackingEnabled(true)]
public class DetectOpenFiles// : ServicedComponent
{
private static Dictionary<string, string> deviceMap;
private const string networkDevicePrefix = "\\Device\\LanmanRedirector\\";
private const int MAX_PATH = 260;
private enum SystemHandleType
{
OB_TYPE_UNKNOWN = 0,
OB_TYPE_TYPE = 1,
OB_TYPE_DIRECTORY,
OB_TYPE_SYMBOLIC_LINK,
OB_TYPE_TOKEN,
OB_TYPE_PROCESS,
OB_TYPE_THREAD,
OB_TYPE_UNKNOWN_7,
OB_TYPE_EVENT,
OB_TYPE_EVENT_PAIR,
OB_TYPE_MUTANT,
OB_TYPE_UNKNOWN_11,
OB_TYPE_SEMAPHORE,
OB_TYPE_TIMER,
OB_TYPE_PROFILE,
OB_TYPE_WINDOW_STATION,
OB_TYPE_DESKTOP,
OB_TYPE_SECTION,
OB_TYPE_KEY,
OB_TYPE_PORT,
OB_TYPE_WAITABLE_PORT,
OB_TYPE_UNKNOWN_21,
OB_TYPE_UNKNOWN_22,
OB_TYPE_UNKNOWN_23,
OB_TYPE_UNKNOWN_24,
//OB_TYPE_CONTROLLER,
//OB_TYPE_DEVICE,
//OB_TYPE_DRIVER,
OB_TYPE_IO_COMPLETION,
OB_TYPE_FILE
};
private const int handleTypeTokenCount = 27;
private static readonly string[] handleTypeTokens = new string[] {
"", "", "Directory", "SymbolicLink", "Token",
"Process", "Thread", "Unknown7", "Event", "EventPair", "Mutant",
"Unknown11", "Semaphore", "Timer", "Profile", "WindowStation",
"Desktop", "Section", "Key", "Port", "WaitablePort",
"Unknown21", "Unknown22", "Unknown23", "Unknown24",
"IoCompletion", "File"
};
[StructLayout(LayoutKind.Sequential)]
private struct SYSTEM_HANDLE_ENTRY
{
public int OwnerPid;
public byte ObjectType;
public byte HandleFlags;
public short HandleValue;
public int ObjectPointer;
public int AccessMask;
}
/// <summary>
/// Gets the open files enumerator.
/// </summary>
/// <param name="processId">The process id.</param>
/// <returns></returns>
public static IEnumerable<String> GetOpenFilesEnumerator(int processId)
{
return new OpenFiles(processId);
}
public static List<Process> GetProcessesUsingFile(string fName)
{
List<Process> result = new List<Process>();
foreach (var p in Process.GetProcesses())
{
try
{
if (DetectOpenFiles.GetOpenFilesEnumerator(p.Id).Contains(fName))
{
result.Add(p);
}
}
catch { }//some processes will fail
}
return result;
}
private sealed class OpenFiles : IEnumerable<String>
{
private readonly int processId;
internal OpenFiles(int processId)
{
this.processId = processId;
}
#region IEnumerable<FileSystemInfo> Members
public IEnumerator<String> GetEnumerator()
{
NT_STATUS ret;
int length = 0x10000;
// Loop, probing for required memory.
do
{
IntPtr ptr = IntPtr.Zero;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
RuntimeHelpers.PrepareConstrainedRegions();
try { }
finally
{
// CER guarantees that the address of the allocated
// memory is actually assigned to ptr if an
// asynchronous exception occurs.
ptr = Marshal.AllocHGlobal(length);
}
int returnLength;
ret = NativeMethods.NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS.SystemHandleInformation, ptr, length, out returnLength);
if (ret == NT_STATUS.STATUS_INFO_LENGTH_MISMATCH)
{
// Round required memory up to the nearest 64KB boundary.
length = ((returnLength + 0xffff) & ~0xffff);
}
else if (ret == NT_STATUS.STATUS_SUCCESS)
{
int handleCount = Marshal.ReadInt32(ptr);
int offset = sizeof(int);
int size = Marshal.SizeOf(typeof(SYSTEM_HANDLE_ENTRY));
for (int i = 0; i < handleCount; i++)
{
SYSTEM_HANDLE_ENTRY handleEntry = (SYSTEM_HANDLE_ENTRY)Marshal.PtrToStructure((IntPtr)((int)ptr + offset), typeof(SYSTEM_HANDLE_ENTRY));
if (handleEntry.OwnerPid == processId)
{
IntPtr handle = (IntPtr)handleEntry.HandleValue;
SystemHandleType handleType;
if (GetHandleType(handle, handleEntry.OwnerPid, out handleType) && handleType == SystemHandleType.OB_TYPE_FILE)
{
string devicePath;
if (GetFileNameFromHandle(handle, handleEntry.OwnerPid, out devicePath))
{
string dosPath;
if (ConvertDevicePathToDosPath(devicePath, out dosPath))
{
if (File.Exists(dosPath))
{
yield return dosPath; // return new FileInfo(dosPath);
}
else if (Directory.Exists(dosPath))
{
yield return dosPath; // new DirectoryInfo(dosPath);
}
}
}
}
}
offset += size;
}
}
}
finally
{
// CER guarantees that the allocated memory is freed,
// if an asynchronous exception occurs.
Marshal.FreeHGlobal(ptr);
//sw.Flush();
//sw.Close();
}
}
while (ret == NT_STATUS.STATUS_INFO_LENGTH_MISMATCH);
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
#region Private Members
private static bool GetFileNameFromHandle(IntPtr handle, int processId, out string fileName)
{
IntPtr currentProcess = NativeMethods.GetCurrentProcess();
bool remote = (processId != NativeMethods.GetProcessId(currentProcess));
SafeProcessHandle processHandle = null;
SafeObjectHandle objectHandle = null;
try
{
if (remote)
{
processHandle = NativeMethods.OpenProcess(ProcessAccessRights.PROCESS_DUP_HANDLE, true, processId);
if (NativeMethods.DuplicateHandle(processHandle.DangerousGetHandle(), handle, currentProcess, out objectHandle, 0, false, DuplicateHandleOptions.DUPLICATE_SAME_ACCESS))
{
handle = objectHandle.DangerousGetHandle();
}
}
return GetFileNameFromHandle(handle, out fileName, 200);
}
finally
{
if (remote)
{
if (processHandle != null)
{
processHandle.Close();
}
if (objectHandle != null)
{
objectHandle.Close();
}
}
}
}
private static bool GetFileNameFromHandle(IntPtr handle, out string fileName, int wait)
{
using (FileNameFromHandleState f = new FileNameFromHandleState(handle))
{
ThreadPool.QueueUserWorkItem(new WaitCallback(GetFileNameFromHandle), f);
if (f.WaitOne(wait))
{
fileName = f.FileName;
return f.RetValue;
}
else
{
fileName = string.Empty;
return false;
}
}
}
private class FileNameFromHandleState : IDisposable
{
private ManualResetEvent _mr;
private IntPtr _handle;
private string _fileName;
private bool _retValue;
public IntPtr Handle
{
get
{
return _handle;
}
}
public string FileName
{
get
{
return _fileName;
}
set
{
_fileName = value;
}
}
public bool RetValue
{
get
{
return _retValue;
}
set
{
_retValue = value;
}
}
public FileNameFromHandleState(IntPtr handle)
{
_mr = new ManualResetEvent(false);
this._handle = handle;
}
public bool WaitOne(int wait)
{
return _mr.WaitOne(wait, false);
}
public void Set()
{
try
{
_mr.Set();
}
catch{}
}
#region IDisposable Members
public void Dispose()
{
if (_mr != null)
_mr.Close();
}
#endregion
}
private static void GetFileNameFromHandle(object state)
{
FileNameFromHandleState s = (FileNameFromHandleState)state;
string fileName;
s.RetValue = GetFileNameFromHandle(s.Handle, out fileName);
s.FileName = fileName;
s.Set();
}
private static bool GetFileNameFromHandle(IntPtr handle, out string fileName)
{
IntPtr ptr = IntPtr.Zero;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
int length = 0x200; // 512 bytes
RuntimeHelpers.PrepareConstrainedRegions();
try { }
finally
{
// CER guarantees the assignment of the allocated
// memory address to ptr, if an ansynchronous exception
// occurs.
ptr = Marshal.AllocHGlobal(length);
}
NT_STATUS ret = NativeMethods.NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectNameInformation, ptr, length, out length);
if (ret == NT_STATUS.STATUS_BUFFER_OVERFLOW)
{
RuntimeHelpers.PrepareConstrainedRegions();
try { }
finally
{
// CER guarantees that the previous allocation is freed,
// and that the newly allocated memory address is
// assigned to ptr if an asynchronous exception occurs.
Marshal.FreeHGlobal(ptr);
ptr = Marshal.AllocHGlobal(length);
}
ret = NativeMethods.NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectNameInformation, ptr, length, out length);
}
if (ret == NT_STATUS.STATUS_SUCCESS)
{
fileName = Marshal.PtrToStringUni((IntPtr)((int)ptr + 8), (length - 9) / 2);
return fileName.Length != 0;
}
}
finally
{
// CER guarantees that the allocated memory is freed,
// if an asynchronous exception occurs.
Marshal.FreeHGlobal(ptr);
}
fileName = string.Empty;
return false;
}
private static bool GetHandleType(IntPtr handle, int processId, out SystemHandleType handleType)
{
string token = GetHandleTypeToken(handle, processId);
return GetHandleTypeFromToken(token, out handleType);
}
private static bool GetHandleType(IntPtr handle, out SystemHandleType handleType)
{
string token = GetHandleTypeToken(handle);
return GetHandleTypeFromToken(token, out handleType);
}
private static bool GetHandleTypeFromToken(string token, out SystemHandleType handleType)
{
for (int i = 1; i < handleTypeTokenCount; i++)
{
if (handleTypeTokens[i] == token)
{
handleType = (SystemHandleType)i;
return true;
}
}
handleType = SystemHandleType.OB_TYPE_UNKNOWN;
return false;
}
private static string GetHandleTypeToken(IntPtr handle, int processId)
{
IntPtr currentProcess = NativeMethods.GetCurrentProcess();
bool remote = (processId != NativeMethods.GetProcessId(currentProcess));
SafeProcessHandle processHandle = null;
SafeObjectHandle objectHandle = null;
try
{
if (remote)
{
processHandle = NativeMethods.OpenProcess(ProcessAccessRights.PROCESS_DUP_HANDLE, true, processId);
if (NativeMethods.DuplicateHandle(processHandle.DangerousGetHandle(), handle, currentProcess, out objectHandle, 0, false, DuplicateHandleOptions.DUPLICATE_SAME_ACCESS))
{
handle = objectHandle.DangerousGetHandle();
}
}
return GetHandleTypeToken(handle);
}
finally
{
if (remote)
{
if (processHandle != null)
{
processHandle.Close();
}
if (objectHandle != null)
{
objectHandle.Close();
}
}
}
}
private static string GetHandleTypeToken(IntPtr handle)
{
int length;
NativeMethods.NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectTypeInformation, IntPtr.Zero, 0, out length);
IntPtr ptr = IntPtr.Zero;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
RuntimeHelpers.PrepareConstrainedRegions();
try { }
finally
{
ptr = Marshal.AllocHGlobal(length);
}
if (NativeMethods.NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectTypeInformation, ptr, length, out length) == NT_STATUS.STATUS_SUCCESS)
{
return Marshal.PtrToStringUni((IntPtr)((int)ptr + 0x60));
}
}
finally
{
Marshal.FreeHGlobal(ptr);
}
return string.Empty;
}
private static bool ConvertDevicePathToDosPath(string devicePath, out string dosPath)
{
EnsureDeviceMap();
int i = devicePath.Length;
while (i > 0 && (i = devicePath.LastIndexOf('\\', i - 1)) != -1)
{
string drive;
if (deviceMap.TryGetValue(devicePath.Substring(0, i), out drive))
{
dosPath = string.Concat(drive, devicePath.Substring(i));
return dosPath.Length != 0;
}
}
dosPath = string.Empty;
return false;
}
private static void EnsureDeviceMap()
{
if (deviceMap == null)
{
Dictionary<string, string> localDeviceMap = BuildDeviceMap();
Interlocked.CompareExchange<Dictionary<string, string>>(ref deviceMap, localDeviceMap, null);
}
}
private static Dictionary<string, string> BuildDeviceMap()
{
string[] logicalDrives = Environment.GetLogicalDrives();
Dictionary<string, string> localDeviceMap = new Dictionary<string, string>(logicalDrives.Length);
StringBuilder lpTargetPath = new StringBuilder(MAX_PATH);
foreach (string drive in logicalDrives)
{
string lpDeviceName = drive.Substring(0, 2);
NativeMethods.QueryDosDevice(lpDeviceName, lpTargetPath, MAX_PATH);
localDeviceMap.Add(NormalizeDeviceName(lpTargetPath.ToString()), lpDeviceName);
}
localDeviceMap.Add(networkDevicePrefix.Substring(0, networkDevicePrefix.Length - 1), "\\");
return localDeviceMap;
}
private static string NormalizeDeviceName(string deviceName)
{
if (string.Compare(deviceName, 0, networkDevicePrefix, 0, networkDevicePrefix.Length, StringComparison.InvariantCulture) == 0)
{
string shareName = deviceName.Substring(deviceName.IndexOf('\\', networkDevicePrefix.Length) + 1);
return string.Concat(networkDevicePrefix, shareName);
}
return deviceName;
}
#endregion
}
}
Not very straightforward, but on Windows Vista and above you can use the Restart Manager APIs to see who is using a file. Internet Explorer caches settings includes details on using this to detect which process has iexplore.exe open.
Omitting a lot of detail:
// Start an RM session
RmStartSession(&sessionHandle, 0, sessionKey);
// Register the file you are checking
RmRegisterResources(sessionHandle, 1, filePathArray, 0, NULL, 0, NULL);
// Get all processes that have that file open.
RmGetList(sessionHAndle, &nProcInfoNeeded, &nProcInfo, processes, &rebootReason);
RmEndSession(sessionHandle);
Handle, from Windows Sysinternals. This is a free command-line utility provided by Microsoft.
You could run it, and parse the result.
I had issues with stefan's solution. Below is a modified version which seems to work well.
using System;
using System.Collections;
using System.Diagnostics;
using System.Management;
using System.IO;
static class Module1
{
static internal ArrayList myProcessArray = new ArrayList();
private static Process myProcess;
public static void Main()
{
string strFile = "c:\\windows\\system32\\msi.dll";
ArrayList a = getFileProcesses(strFile);
foreach (Process p in a)
{
Debug.Print(p.ProcessName);
}
}
private static ArrayList getFileProcesses(string strFile)
{
myProcessArray.Clear();
Process[] processes = Process.GetProcesses();
int i = 0;
for (i = 0; i <= processes.GetUpperBound(0) - 1; i++)
{
myProcess = processes[i];
//if (!myProcess.HasExited) //This will cause an "Access is denied" error
if (myProcess.Threads.Count > 0)
{
try
{
ProcessModuleCollection modules = myProcess.Modules;
int j = 0;
for (j = 0; j <= modules.Count - 1; j++)
{
if ((modules[j].FileName.ToLower().CompareTo(strFile.ToLower()) == 0))
{
myProcessArray.Add(myProcess);
break;
// TODO: might not be correct. Was : Exit For
}
}
}
catch (Exception exception)
{
//MsgBox(("Error : " & exception.Message))
}
}
}
return myProcessArray;
}
}
UPDATE
If you just want to know which process(es) are locking a particular DLL, you can execute and parse the output of tasklist /m YourDllName.dll. Works on Windows XP and later. See
What does this do? tasklist /m "mscor*"
This works for DLLs locked by other processes. This routine will not find out for example that a text file is locked by a word process.
C#:
using System.Management;
using System.IO;
static class Module1
{
static internal ArrayList myProcessArray = new ArrayList();
private static Process myProcess;
public static void Main()
{
string strFile = "c:\\windows\\system32\\msi.dll";
ArrayList a = getFileProcesses(strFile);
foreach (Process p in a) {
Debug.Print(p.ProcessName);
}
}
private static ArrayList getFileProcesses(string strFile)
{
myProcessArray.Clear();
Process[] processes = Process.GetProcesses;
int i = 0;
for (i = 0; i <= processes.GetUpperBound(0) - 1; i++) {
myProcess = processes(i);
if (!myProcess.HasExited) {
try {
ProcessModuleCollection modules = myProcess.Modules;
int j = 0;
for (j = 0; j <= modules.Count - 1; j++) {
if ((modules.Item(j).FileName.ToLower.CompareTo(strFile.ToLower) == 0)) {
myProcessArray.Add(myProcess);
break; // TODO: might not be correct. Was : Exit For
}
}
}
catch (Exception exception) {
}
//MsgBox(("Error : " & exception.Message))
}
}
return myProcessArray;
}
}
VB.Net:
Imports System.Management
Imports System.IO
Module Module1
Friend myProcessArray As New ArrayList
Private myProcess As Process
Sub Main()
Dim strFile As String = "c:\windows\system32\msi.dll"
Dim a As ArrayList = getFileProcesses(strFile)
For Each p As Process In a
Debug.Print(p.ProcessName)
Next
End Sub
Private Function getFileProcesses(ByVal strFile As String) As ArrayList
myProcessArray.Clear()
Dim processes As Process() = Process.GetProcesses
Dim i As Integer
For i = 0 To processes.GetUpperBound(0) - 1
myProcess = processes(i)
If Not myProcess.HasExited Then
Try
Dim modules As ProcessModuleCollection = myProcess.Modules
Dim j As Integer
For j = 0 To modules.Count - 1
If (modules.Item(j).FileName.ToLower.CompareTo(strFile.ToLower) = 0) Then
myProcessArray.Add(myProcess)
Exit For
End If
Next j
Catch exception As Exception
'MsgBox(("Error : " & exception.Message))
End Try
End If
Next i
Return myProcessArray
End Function
End Module
The following was produced based on Iain Ballard's code dump. It is broken: it will occasionally lock up when you retrieve the handle name. This code doesn't contain any work-arounds for that issue, and .NET leaves few options: Thread.Abort can no longer abort a thread that's currently in a native method.
So, with that disclaimer, here is the code to retrieve handles which has been adapted to work (apart from the occasional lock-up) both in 32 and 64 bit modes:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
namespace BrokenHandleRetrieval
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Enumerates open handles.");
Console.WriteLine("This *will* lock up on calling HandleInfo.Name from time to time. Thread.Abort() won't help.");
foreach (var hi in HandleUtil.GetHandles().Where(hi => hi.Type == HandleType.File))
Console.WriteLine("pid: " + hi.ProcessId + ", name: " + hi.Name);
}
}
public enum HandleType
{
Unknown,
Other,
File, Directory, SymbolicLink, Key,
Process, Thread, Job, Session, WindowStation,
Timer, Desktop, Semaphore, Token,
Mutant, Section, Event, KeyedEvent, IoCompletion, IoCompletionReserve,
TpWorkerFactory, AlpcPort, WmiGuid, UserApcReserve,
}
public class HandleInfo
{
public int ProcessId { get; private set; }
public ushort Handle { get; private set; }
public int GrantedAccess { get; private set; }
public byte RawType { get; private set; }
public HandleInfo(int processId, ushort handle, int grantedAccess, byte rawType)
{
ProcessId = processId;
Handle = handle;
GrantedAccess = grantedAccess;
RawType = rawType;
}
private static Dictionary<byte, string> _rawTypeMap = new Dictionary<byte, string>();
private string _name, _typeStr;
private HandleType _type;
public string Name { get { if (_name == null) initTypeAndName(); return _name; } }
public string TypeString { get { if (_typeStr == null) initType(); return _typeStr; } }
public HandleType Type { get { if (_typeStr == null) initType(); return _type; } }
private void initType()
{
if (_rawTypeMap.ContainsKey(RawType))
{
_typeStr = _rawTypeMap[RawType];
_type = HandleTypeFromString(_typeStr);
}
else
initTypeAndName();
}
bool _typeAndNameAttempted = false;
private void initTypeAndName()
{
if (_typeAndNameAttempted)
return;
_typeAndNameAttempted = true;
IntPtr sourceProcessHandle = IntPtr.Zero;
IntPtr handleDuplicate = IntPtr.Zero;
try
{
sourceProcessHandle = NativeMethods.OpenProcess(0x40 /* dup_handle */, true, ProcessId);
// To read info about a handle owned by another process we must duplicate it into ours
// For simplicity, current process handles will also get duplicated; remember that process handles cannot be compared for equality
if (!NativeMethods.DuplicateHandle(sourceProcessHandle, (IntPtr) Handle, NativeMethods.GetCurrentProcess(), out handleDuplicate, 0, false, 2 /* same_access */))
return;
// Query the object type
if (_rawTypeMap.ContainsKey(RawType))
_typeStr = _rawTypeMap[RawType];
else
{
int length;
NativeMethods.NtQueryObject(handleDuplicate, OBJECT_INFORMATION_CLASS.ObjectTypeInformation, IntPtr.Zero, 0, out length);
IntPtr ptr = IntPtr.Zero;
try
{
ptr = Marshal.AllocHGlobal(length);
if (NativeMethods.NtQueryObject(handleDuplicate, OBJECT_INFORMATION_CLASS.ObjectTypeInformation, ptr, length, out length) != NT_STATUS.STATUS_SUCCESS)
return;
_typeStr = Marshal.PtrToStringUni((IntPtr) ((int) ptr + 0x58 + 2 * IntPtr.Size));
_rawTypeMap[RawType] = _typeStr;
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}
_type = HandleTypeFromString(_typeStr);
// Query the object name
if (_typeStr != null && GrantedAccess != 0x0012019f && GrantedAccess != 0x00120189 && GrantedAccess != 0x120089) // don't query some objects that could get stuck
{
int length;
NativeMethods.NtQueryObject(handleDuplicate, OBJECT_INFORMATION_CLASS.ObjectNameInformation, IntPtr.Zero, 0, out length);
IntPtr ptr = IntPtr.Zero;
try
{
ptr = Marshal.AllocHGlobal(length);
if (NativeMethods.NtQueryObject(handleDuplicate, OBJECT_INFORMATION_CLASS.ObjectNameInformation, ptr, length, out length) != NT_STATUS.STATUS_SUCCESS)
return;
_name = Marshal.PtrToStringUni((IntPtr) ((int) ptr + 2 * IntPtr.Size));
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}
}
finally
{
NativeMethods.CloseHandle(sourceProcessHandle);
if (handleDuplicate != IntPtr.Zero)
NativeMethods.CloseHandle(handleDuplicate);
}
}
public static HandleType HandleTypeFromString(string typeStr)
{
switch (typeStr)
{
case null: return HandleType.Unknown;
case "File": return HandleType.File;
case "IoCompletion": return HandleType.IoCompletion;
case "TpWorkerFactory": return HandleType.TpWorkerFactory;
case "ALPC Port": return HandleType.AlpcPort;
case "Event": return HandleType.Event;
case "Section": return HandleType.Section;
case "Directory": return HandleType.Directory;
case "KeyedEvent": return HandleType.KeyedEvent;
case "Process": return HandleType.Process;
case "Key": return HandleType.Key;
case "SymbolicLink": return HandleType.SymbolicLink;
case "Thread": return HandleType.Thread;
case "Mutant": return HandleType.Mutant;
case "WindowStation": return HandleType.WindowStation;
case "Timer": return HandleType.Timer;
case "Semaphore": return HandleType.Semaphore;
case "Desktop": return HandleType.Desktop;
case "Token": return HandleType.Token;
case "Job": return HandleType.Job;
case "Session": return HandleType.Session;
case "IoCompletionReserve": return HandleType.IoCompletionReserve;
case "WmiGuid": return HandleType.WmiGuid;
case "UserApcReserve": return HandleType.UserApcReserve;
default: return HandleType.Other;
}
}
}
public static class HandleUtil
{
public static IEnumerable<HandleInfo> GetHandles()
{
// Attempt to retrieve the handle information
int length = 0x10000;
IntPtr ptr = IntPtr.Zero;
try
{
while (true)
{
ptr = Marshal.AllocHGlobal(length);
int wantedLength;
var result = NativeMethods.NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS.SystemHandleInformation, ptr, length, out wantedLength);
if (result == NT_STATUS.STATUS_INFO_LENGTH_MISMATCH)
{
length = Math.Max(length, wantedLength);
Marshal.FreeHGlobal(ptr);
ptr = IntPtr.Zero;
}
else if (result == NT_STATUS.STATUS_SUCCESS)
break;
else
throw new Exception("Failed to retrieve system handle information.");
}
int handleCount = IntPtr.Size == 4 ? Marshal.ReadInt32(ptr) : (int) Marshal.ReadInt64(ptr);
int offset = IntPtr.Size;
int size = Marshal.SizeOf(typeof(SystemHandleEntry));
for (int i = 0; i < handleCount; i++)
{
var struc = (SystemHandleEntry) Marshal.PtrToStructure((IntPtr) ((int) ptr + offset), typeof(SystemHandleEntry));
yield return new HandleInfo(struc.OwnerProcessId, struc.Handle, struc.GrantedAccess, struc.ObjectTypeNumber);
offset += size;
}
}
finally
{
if (ptr != IntPtr.Zero)
Marshal.FreeHGlobal(ptr);
}
}
[StructLayout(LayoutKind.Sequential)]
private struct SystemHandleEntry
{
public int OwnerProcessId;
public byte ObjectTypeNumber;
public byte Flags;
public ushort Handle;
public IntPtr Object;
public int GrantedAccess;
}
}
enum NT_STATUS
{
STATUS_SUCCESS = 0x00000000,
STATUS_BUFFER_OVERFLOW = unchecked((int) 0x80000005L),
STATUS_INFO_LENGTH_MISMATCH = unchecked((int) 0xC0000004L)
}
enum SYSTEM_INFORMATION_CLASS
{
SystemBasicInformation = 0,
SystemPerformanceInformation = 2,
SystemTimeOfDayInformation = 3,
SystemProcessInformation = 5,
SystemProcessorPerformanceInformation = 8,
SystemHandleInformation = 16,
SystemInterruptInformation = 23,
SystemExceptionInformation = 33,
SystemRegistryQuotaInformation = 37,
SystemLookasideInformation = 45
}
enum OBJECT_INFORMATION_CLASS
{
ObjectBasicInformation = 0,
ObjectNameInformation = 1,
ObjectTypeInformation = 2,
ObjectAllTypesInformation = 3,
ObjectHandleInformation = 4
}
static class NativeMethods
{
[DllImport("ntdll.dll")]
internal static extern NT_STATUS NtQuerySystemInformation(
[In] SYSTEM_INFORMATION_CLASS SystemInformationClass,
[In] IntPtr SystemInformation,
[In] int SystemInformationLength,
[Out] out int ReturnLength);
[DllImport("ntdll.dll")]
internal static extern NT_STATUS NtQueryObject(
[In] IntPtr Handle,
[In] OBJECT_INFORMATION_CLASS ObjectInformationClass,
[In] IntPtr ObjectInformation,
[In] int ObjectInformationLength,
[Out] out int ReturnLength);
[DllImport("kernel32.dll")]
internal static extern IntPtr GetCurrentProcess();
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(
[In] int dwDesiredAccess,
[In, MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
[In] int dwProcessId);
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool CloseHandle(
[In] IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DuplicateHandle(
[In] IntPtr hSourceProcessHandle,
[In] IntPtr hSourceHandle,
[In] IntPtr hTargetProcessHandle,
[Out] out IntPtr lpTargetHandle,
[In] int dwDesiredAccess,
[In, MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
[In] int dwOptions);
}
}
This is probably irrelevant and if it is please someone comment but there was a work-around I've used in explorer before to get around file locks.
If a file was locked by a process that had died Windows often wouldn't let you delete it but if you created a new file of the same name somewhere else, moved it to the folder it would succeed. You could then delete the new file and all was well.
To use this for your app you'd have to be able to read the file and hold it in memory before you did this then you write it back out after you'd got rid of the old one.
Maybe it will help, maybe not but it's worth trying.
Try Unlocker. If you try and delete the file that is locked by another process, it will list the process(es) that have the file locked. You can then unlock the file by shutting down those processes.
foreach (var process in Process.GetProcessesByName("excel")) //whatever you need to close
{
if (process.MainWindowTitle.Contains("test.xlsx"))
{
process.Kill();
break;
}
}
or
foreach (var process in Process.GetProcesses())
{
if (process.MainWindowTitle.Contains("test.dat"))
{
process.Kill();
break;
}
}
I believe that you need code running in kernel mode to completely answer the question (but I haven't looked at the restart manager API).
You can enumerate all processes and their modules - so if the file you're looking for is a module (DLL, EXE, OCX...), you're good to go. But if it's a text file for example, you have to look at the kernel handle table which you cannot see from user mode. Handle.exe has a kernel driver in order to do that.
I rewrote the GetProcessesLockingFile() method in the solution. The code was not working.
For example, you have a folder "C:\folder1\folder2" and a process in folder2 (process1). If the process was running, GetProcessesLockingFile() was returning "C:\folder1\folder2". So the condition if (files.Contains(filePath)) => if ("C:\folder1\folder2".contains("C:\folder1\folder2\process1")) was never true.
So this is my solution:
public static List<Process> GetProcessesLockingFile(FileInfo file)
{
var procs = new List<Process>();
var processListSnapshot = Process.GetProcesses();
foreach (var process in processListSnapshot)
{
if (process.Id <= 4) { continue; } // system processes
List<string> paths = GetFilesLockedBy(process);
foreach (string path in paths)
{
string pathDirectory = path;
if (!pathDirectory.EndsWith(Constants.DOUBLE_BACKSLASH))
{
pathDirectory = pathDirectory + Constants.DOUBLE_BACKSLASH;
}
string lastFolderName = Path.GetFileName(Path.GetDirectoryName(pathDirectory));
if (file.FullName.Contains(lastFolderName))
{
procs.Add(process);
}
}
}
return procs;
}
Or with a string parameter:
public static List<Process> GetProcessesLockingFile(string filePath)
{
var procs = new List<Process>();
var processListSnapshot = Process.GetProcesses();
foreach (var process in processListSnapshot)
{
if (process.Id <= 4) { continue; } // system processes
List<string> paths = GetFilesLockedBy(process);
foreach (string path in paths)
{
string pathDirectory = path;
if (!pathDirectory.EndsWith(Constants.DOUBLE_BACKSLASH))
{
pathDirectory = pathDirectory + Constants.DOUBLE_BACKSLASH;
}
string lastFolderName = Path.GetFileName(Path.GetDirectoryName(pathDirectory));
if (filePath.Contains(lastFolderName))
{
procs.Add(process);
}
}
}
return procs;
}
You absolutely don't need to run in Kernel mode (!!!)
It's a Win32 FAQ since Windows 95 (!) (in C, Google groups, Win32) : read the handle table, from User mode of course, and get the PID from the File handle ...
Using dotnet core (net6) I solved this problem by using the win32 restart manager (as others have also mentioned). However some of the linked articles have elaborate code importing DLLs and calling those.
After finding an app to kill processes that lock a file written by meziantou. I found out that he publishes .Net wrappers for win32 dlls (including the restart manager).
Leveraging his work, I was able to fix this problem with the following code:
using Meziantou.Framework.Win32;
public static IEnumerable<Process> GetProcessesLockingFile(string filePath)
{
using var session = RestartManager.CreateSession();
session.RegisterFile(filePath);
return session.GetProcessesLockingResources();
}
simpler with linq:
public void KillProcessesAssociatedToFile(string file)
{
GetProcessesAssociatedToFile(file).ForEach(x =>
{
x.Kill();
x.WaitForExit(10000);
});
}
public List<Process> GetProcessesAssociatedToFile(string file)
{
return Process.GetProcesses()
.Where(x => !x.HasExited
&& x.Modules.Cast<ProcessModule>().ToList()
.Exists(y => y.FileName.ToLowerInvariant() == file.ToLowerInvariant())
).ToList();
}
Related
PInvoke NetLocalGroupGetMembers runs into FatalExecutionEngineError
I need to use win32 NetLocalGroupGetMembers in C#. I found and tested three solutions. All three fail with an FatalExecutionEngineError. The framework is .net 4.0 Here is a full example: Reference to the api: static class NetworkAPI { [DllImport("Netapi32.dll")] public extern static int NetLocalGroupGetMembers([MarshalAs(UnmanagedType.LPWStr)] string servername, [MarshalAs(UnmanagedType.LPWStr)] string localgroupname, int level, out IntPtr bufptr, int prefmaxlen, out int entriesread, out int totalentries, out int resumehandle); [DllImport("Netapi32.dll")] public extern static int NetApiBufferFree(IntPtr Buffer); // LOCALGROUP_MEMBERS_INFO_1 - Structure for holding members details [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct LOCALGROUP_MEMBERS_INFO_1 { public int lgrmi1_sid; public int lgrmi1_sidusage; public string lgrmi1_name; } } calling the function: static void Main(string[] args) { int EntriesRead; int TotalEntries; int Resume; IntPtr bufPtr; string groupName = "Administrators"; NetworkAPI.NetLocalGroupGetMembers(null, groupName, 1, out bufPtr, -1, out EntriesRead, out TotalEntries, out Resume); if (EntriesRead > 0) { NetworkAPI.LOCALGROUP_MEMBERS_INFO_1[] Members = new NetworkAPI.LOCALGROUP_MEMBERS_INFO_1[EntriesRead]; IntPtr iter = bufPtr; // EntriesRead has the correct quantity of members of the group, so the group is found for (int i = 0; i < EntriesRead; i++) { // -------------------------------------------------- // ==> here the FatalExecutionEngineError happens: Members[i] = (NetworkAPI.LOCALGROUP_MEMBERS_INFO_1)Marshal.PtrToStructure(iter, typeof(NetworkAPI.LOCALGROUP_MEMBERS_INFO_1)); // // -------------------------------------------------- iter = (IntPtr)((int)iter + Marshal.SizeOf(typeof(NetworkAPI.LOCALGROUP_MEMBERS_INFO_1))); Console.WriteLine(Members[i].lgrmi1_name); } NetworkAPI.NetApiBufferFree(bufPtr); } }
I see the following errors: The resume handle is a pointer. Use ref IntPtr resumehandle for that parameter, and pass IntPtr.Zero on the first call. Or if you don't need to use a resume handle declare the parameter as IntPtr resumehandle and pass IntPtr.Zero. Consult the function documentation on MSDN for the full details. The lgrmi1_sid member of the struct is a pointer. Declare it as such: public IntPtr lgrmi1_sid. Casting an IntPtr to an int will lead to pointer truncation on 64 bit. Either use arithmetic directly on the IntPtr, or for older C# versions cast to long. The former is better, like so: iter += Marshal.SizeOf(typeof(NetworkAPI.LOCALGROUP_MEMBERS_INFO_1));. You do not check the return value for errors. Fix those errors and your program will run correctly.
For the sake of completeness, here is the code how to pinvoke NetLocalGroupGetMembers. I corrected the code as David suggested. There is also a suggestion from Martin Liversage which I didn't implement. But it maybe usefull. If you like it, please do not upvode this answer but upvote Davids answer, who found the errors. Reference to the api: public static class NetworkAPI { [DllImport("Netapi32.dll")] public extern static uint NetLocalGroupGetMembers([MarshalAs(UnmanagedType.LPWStr)] string servername, [MarshalAs(UnmanagedType.LPWStr)] string localgroupname, int level, out IntPtr bufptr, int prefmaxlen, out int entriesread, out int totalentries, out IntPtr resumehandle); [DllImport("Netapi32.dll")] public extern static int NetApiBufferFree(IntPtr Buffer); // LOCALGROUP_MEMBERS_INFO_1 - Structure for holding members details [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct LOCALGROUP_MEMBERS_INFO_1 { public IntPtr lgrmi1_sid; public int lgrmi1_sidusage; public string lgrmi1_name; } // documented in MSDN public const uint ERROR_ACCESS_DENIED = 0x0000005; public const uint ERROR_MORE_DATA = 0x00000EA; public const uint ERROR_NO_SUCH_ALIAS = 0x0000560; public const uint NERR_InvalidComputer = 0x000092F; // found by testing public const uint NERR_GroupNotFound = 0x00008AC; public const uint SERVER_UNAVAILABLE = 0x0006BA; } Calling the function: static void Main(string[] args) { int EntriesRead; int TotalEntries; IntPtr Resume; IntPtr bufPtr; string groupName = "Administratoren"; string computerName = null; // null for the local machine uint retVal = NetworkAPI.NetLocalGroupGetMembers(computerName, groupName, 1, out bufPtr, -1, out EntriesRead, out TotalEntries, out Resume); if(retVal != 0) { if (retVal == NetworkAPI.ERROR_ACCESS_DENIED) { Console.WriteLine("Access denied"); return; } if (retVal == NetworkAPI.ERROR_MORE_DATA) { Console.WriteLine("ERROR_MORE_DATA"); return; } if (retVal == NetworkAPI.ERROR_NO_SUCH_ALIAS) { Console.WriteLine("Group not found"); return; } if (retVal == NetworkAPI.NERR_InvalidComputer) { Console.WriteLine("Invalid computer name"); return; } if (retVal == NetworkAPI.NERR_GroupNotFound) { Console.WriteLine("Group not found"); return; } if (retVal == NetworkAPI.SERVER_UNAVAILABLE) { Console.WriteLine("Server unavailable"); return; } Console.WriteLine("Unexpected NET_API_STATUS: " + retVal.ToString()); return; } if (EntriesRead > 0) { NetworkAPI.LOCALGROUP_MEMBERS_INFO_1[] Members = new NetworkAPI.LOCALGROUP_MEMBERS_INFO_1[EntriesRead]; IntPtr iter = bufPtr; for (int i = 0; i < EntriesRead; i++) { Members[i] = (NetworkAPI.LOCALGROUP_MEMBERS_INFO_1)Marshal.PtrToStructure(iter, typeof(NetworkAPI.LOCALGROUP_MEMBERS_INFO_1)); //x64 safe iter += Marshal.SizeOf(typeof(NetworkAPI.LOCALGROUP_MEMBERS_INFO_1)); Console.WriteLine(Members[i].lgrmi1_name); } NetworkAPI.NetApiBufferFree(bufPtr); } }
How to detect if a Windows system supports wake up timers
I need to programmatically detect whether my computer (Windows 7 / 8) supports wake timers. So far I have done the following: Guid activePowerScheme = GetActivePowerSchemeGuid(); IntPtr ptrActiveGuid = IntPtr.Zero; uint buffSize = 0; uint res = PowerReadACValue(IntPtr.Zero, ref activePowerScheme, ref ApplicationConstants.SLEEPGUID, ref ApplicationConstants.WAKETIMERGUID, IntPtr.Zero, IntPtr.Zero, ref buffSize); if (res == 0) { IntPtr ptrName = IntPtr.Zero; try { ptrName = Marshal.AllocHGlobal((int)buffSize); res = PowerReadACValue(IntPtr.Zero, ref activePowerScheme, ref ApplicationConstants.SLEEPGUID, ref ApplicationConstants.WAKETIMERGUID, IntPtr.Zero, ptrName, ref buffSize); byte[] ba = new byte[buffSize]; Marshal.Copy(ptrName, ba, 0, (int)buffSize); int retVal = BitConverter.ToInt32(ba, 0); if (retVal == 0) { return true; } else { return false; } } catch(Exception exp) { Logger.LogException(exp); return false; } finally { if (ptrName != IntPtr.Zero) { Marshal.FreeHGlobal(ptrName); } } } return false; This works most of the time, but when I reset my power plan settings, this doesn't work well (inconsistent). I also tried the following: Guid currentPowerSchemeGuid = GetActivePowerSchemeGuid(); RegistryKey currentPowerSchemeKey = Registry.LocalMachine.OpenSubKey(#"SYSTEM\CurrentControlSet\Control\Power\User\PowerSchemes\" + currentPowerSchemeGuid.ToString()); if (currentPowerSchemeKey != null) { RegistryKey sleepRegKey = currentPowerSchemeKey.OpenSubKey(ApplicationConstants.SLEEPGUID.ToString()); currentPowerSchemeKey.Close(); if (sleepRegKey != null) { RegistryKey wakeTimerRegKey = sleepRegey.OpenSubKey(ApplicationConstants.WAKETIMERGUID.ToString()); sleepRegKey.Close(); if (wakeTimerRegKey != null) { wakeTimerRegKey.Close(); currentPowerSchemeKey.Close(); return true; } else { currentPowerSchemeKey.Close(); return false; } } else { currentPowerSchemeKey.Close(); return false; } } else { return false; } This doesn't work on reset of power plan settings, the wake timer GUID registry key gets cleared. Is there a proper way I can detect if my system supports wake timers?
According to arx, tried the following code and it works. public static bool IsWakeTimerSupported() { IntPtr timerHandle = CreateWaitableTimer(IntPtr.Zero, true, "Wait Timer 1"); uint retVal = GetLastError(); if (timerHandle != IntPtr.Zero) { CancelWaitableTimer(timerHandle); CloseHandle(timerHandle); timerHandle = IntPtr.Zero; } //SUCCESS if (retVal == 0) { return true; } else { return false; } } The CancelWaitableTimer(timerHandle) can be ignored as the MSDN documentation says to use CloseHandle. EDIT: public static bool IsWakeTimerSupported() { IntPtr timerHandle = CreateWaitableTimer(IntPtr.Zero, true, "Wait Timer 1"); long interval = 0; int retVal = 0; if (timerHandle != IntPtr.Zero) { SetWaitableTimer(timerHandle, ref interval, 0, IntPtr.Zero, IntPtr.Zero, true); retVal = Marshal.GetLastWin32Error(); WaitableTimer.CancelWaitableTimer(timerHandle); try { Win32.CloseHandle(timerHandle); } catch (Exception exp) { Logger.LogException(exp); } timerHandle = IntPtr.Zero; } //SUCCESS if (retVal == 0) { return true; } else { return false; } } According to this article, http://blogs.msdn.com/b/adam_nathan/archive/2003/04/25/56643.aspx we shoud never use GetLastError through PInvoke.
Using the powrprof.dll library worked for me: using System; using System.ComponentModel; using System.Runtime.InteropServices; namespace Namespace { public static class PowerOptions { // src: https://msdn.microsoft.com/en-us/library/windows/desktop/hh448380%28v=vs.85%29.aspx private readonly static Guid HIGH_PERFORMANCE = new Guid("8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c"); // aka MIN_POWER_SAVINGS private readonly static Guid BALANCED = new Guid("381b4222-f694-41f0-9685-ff5bb260df2e"); // aka TYPICAL_POWER_SAVINGS private readonly static Guid POWER_SAVER = new Guid("a1841308-3541-4fab-bc81-f71556f20b4a"); // aka MAX_POWER_SAVINGS private readonly static Guid ACDC_POWER_SOURCE = new Guid("5d3e9a59-e9D5-4b00-a6bd-ff34ff516548"); private readonly static Guid SLEEP_SUBCATEGORY = new Guid("238C9FA8-0AAD-41ED-83F4-97BE242C8F20"); private readonly static Guid WAKE_TIMERS = new Guid("bd3b718a-0680-4d9d-8ab2-e1d2b4ac806d"); public static String GetCurrentPowerPlanFriendlyName() { IntPtr ptrActiveGuid = IntPtr.Zero; int ret = PowerGetActiveScheme(IntPtr.Zero, ref ptrActiveGuid); if (ret == 0) { uint buffSize = 0; ret = PowerReadFriendlyName(IntPtr.Zero, ptrActiveGuid, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, ref buffSize); if (ret == 0) { if (buffSize == 0) return ""; IntPtr ptrName = Marshal.AllocHGlobal((int) buffSize); ret = PowerReadFriendlyName(IntPtr.Zero, ptrActiveGuid, IntPtr.Zero, IntPtr.Zero, ptrName, ref buffSize); if (ret == 0) { String name = Marshal.PtrToStringUni(ptrName); Marshal.FreeHGlobal(ptrName); return name; } Marshal.FreeHGlobal(ptrName); } } throw new Win32Exception(ret, "GetCurrentPowerPlanFriendlyName"); } public static PowerStatus GetPowerStatus() { PowerStatus ps = new PowerStatus(); if (!GetSystemPowerStatus(ref ps)) throw new Win32Exception(Marshal.GetLastWin32Error(), "GetPowerStatus"); return ps; } public static bool GetWakeTimersEnabled(PowerSource powerSource = PowerSource.Current, PowerPlan powerPlan = PowerPlan.Current) { int ret = 0; if (powerSource == PowerSource.Current) { PowerStatus ps = GetPowerStatus(); if (ps.ACLineStatus == PowerLineStatus.Online) powerSource = PowerSource.PluggedIn; else powerSource = PowerSource.OnBattery; } if (ret == 0) { if (powerPlan == PowerPlan.Current) { IntPtr ptrPowerPlan = IntPtr.Zero; ret = PowerGetActiveScheme(IntPtr.Zero, ref ptrPowerPlan); if (ret == 0) { uint value = 0; if (powerSource == PowerSource.PluggedIn) ret = PowerReadACValueIndex(IntPtr.Zero, ptrPowerPlan, SLEEP_SUBCATEGORY, WAKE_TIMERS, ref value); else ret = PowerReadDCValueIndex(IntPtr.Zero, ptrPowerPlan, SLEEP_SUBCATEGORY, WAKE_TIMERS, ref value); if (ret == 0) { return (value == 1); } } } else { Guid guid = GetGuid(powerPlan); uint value = 0; if (powerSource == PowerSource.PluggedIn) ret = PowerReadACValueIndex(IntPtr.Zero, guid, SLEEP_SUBCATEGORY, WAKE_TIMERS, ref value); else ret = PowerReadDCValueIndex(IntPtr.Zero, guid, SLEEP_SUBCATEGORY, WAKE_TIMERS, ref value); if (ret == 0) { return (value == 1); } } } throw new Win32Exception(ret, "GetWakeTimersEnabled"); } public static Guid GetGuid(PowerPlan powerPlan) { if (powerPlan == PowerPlan.Balanced) return BALANCED; if (powerPlan == PowerPlan.HighPerformance) return HIGH_PERFORMANCE; if (powerPlan == PowerPlan.PowerSaver) return POWER_SAVER; throw new ArgumentException("Not a standard power plan: " + powerPlan); } [DllImport("powrprof.dll", SetLastError = true)] public static extern int PowerWriteACValueIndex(IntPtr RootPowerKey, Guid SchemeGuid, Guid SubGroupOfPowerSettingsGuid, Guid PowerSettingGuid, uint AcValueIndex); [DllImport("powrprof.dll", SetLastError = true)] private static extern int PowerReadACValueIndex(IntPtr RootPowerKey, IntPtr SchemeGuid, Guid SubGroupOfPowerSettingsGuid, Guid PowerSettingGuid, ref uint AcValueIndex); [DllImport("powrprof.dll", SetLastError = true)] private static extern int PowerReadACValueIndex(IntPtr RootPowerKey, Guid SchemeGuid, Guid SubGroupOfPowerSettingsGuid, Guid PowerSettingGuid, ref uint AcValueIndex); [DllImport("powrprof.dll", SetLastError = true)] private static extern int PowerReadDCValueIndex(IntPtr RootPowerKey, IntPtr SchemeGuid, Guid SubGroupOfPowerSettingsGuid, Guid PowerSettingGuid, ref uint AcValueIndex); [DllImport("powrprof.dll", SetLastError = true)] private static extern int PowerReadDCValueIndex(IntPtr RootPowerKey, Guid SchemeGuid, Guid SubGroupOfPowerSettingsGuid, Guid PowerSettingGuid, ref uint AcValueIndex); [DllImport("powrprof.dll", SetLastError = true)] private static extern int PowerGetActiveScheme(IntPtr UserRootPowerKey, ref IntPtr ActivePolicyGuid); [DllImport("powrprof.dll", SetLastError = true)] private static extern int PowerReadFriendlyName(IntPtr RootPowerKey, IntPtr SchemeGuid, IntPtr SubGroupOfPowerSettingsGuid, IntPtr PowerSettingGuid, IntPtr Buffer, ref uint BufferSize); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool GetSystemPowerStatus(ref PowerStatus lpSystemPowerStatus); } public enum PowerPlan { Current, HighPerformance, Balanced, PowerSaver, } public enum PowerSource { Current, OnBattery, PluggedIn } public struct PowerStatus { ///<summary>The AC power status.</summary> public PowerLineStatus ACLineStatus; ///<summary>The battery charge status.</summary> public PowerChargeStatus BatteryFlag; ///<summary>Returns a value between [0 to 100] or 255 if unknown.</summary> public byte BatteryLifePercent; ///<summary>Returns a value that indicates if the system is currently conserving power.</summary> public PowerSaveStatus SystemStatusFlag; ///<summary>Number of seconds of battery life remaining, or -1 if unknown.</summary> public int BatteryLifeTime; ///<summary>Number of seconds of batter life on a full charge, or -1 if unknown.</summary> public int BatteryFullLifeTime; } public enum PowerLineStatus : byte { Offline = 0, Online = 1, Unknown = 255, } [Flags] public enum PowerChargeStatus : byte { High = 1, Low = 2, Critical = 4, Charging = 8, NoBattery = 128, Unknown = 255, } public enum PowerSaveStatus : byte { Off = 0, On = 1, } }
Delete a mutex from another process
Using the topic Overview - Handle Enumeration, number 5, the attempt Close mutex of another process and and information from Mutex analysis, the canary in the coal mine and discovering new families of malware/, I have came up with: Attempt 1: http://pastebin.com/QU0WBgE5 You must open Notepad first. Needless to say, this is not working for me. I need better error checking to figure out what's going on. I don't know how to get mutex pointers in the format I see them in Process Explorer. My goal is to be able to delete/kill of the mutex handles created by a process so more than one instance can be open. I can do this manually using Process Explorer, but I want to do it programmatically. (Based on Yahia's notes, I need more permissions.) Attempt 2: http://pastebin.com/yyQLhesP At least now I have some sort of error checking, most of the time DuplicateHandle returns 6 or 5, which is an invalid handle and access denied respectfully. Working attempt (kind of): I actually didn't require anything Yahia stated in the end. I was getting a "local" handle when I needed a remote one. Basically, what I mean is that you have to find the HandleValue using NtQuerySystemInformation and use that handle, not the one returned by OpenMutex / CreateMutex. Granted, I can't get it to work on some applications (osk.exe -- on screen keyboard), but it worked for the application I was going for, posting code in case someone wants to take it further. using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Diagnostics; using System.Text; using System.Threading; using System.Security.AccessControl; using System.Security.Principal; namespace FileLockInfo { public class Win32API { [DllImport("ntdll.dll")] public static extern int NtQueryObject(IntPtr ObjectHandle, int ObjectInformationClass, IntPtr ObjectInformation, int ObjectInformationLength, ref int returnLength); [DllImport("kernel32.dll", SetLastError = true)] public static extern uint QueryDosDevice(string lpDeviceName, StringBuilder lpTargetPath, int ucchMax); [DllImport("ntdll.dll")] public static extern uint NtQuerySystemInformation(int SystemInformationClass, IntPtr SystemInformation, int SystemInformationLength, ref int returnLength); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern IntPtr OpenMutex(UInt32 desiredAccess, bool inheritHandle, string name); [DllImport("kernel32.dll")] public static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, int dwProcessId); [DllImport("kernel32.dll")] public static extern int CloseHandle(IntPtr hObject); [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool DuplicateHandle(IntPtr hSourceProcessHandle, ushort hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle, uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwOptions); [DllImport("kernel32.dll")] public static extern IntPtr GetCurrentProcess(); public enum ObjectInformationClass : int { ObjectBasicInformation = 0, ObjectNameInformation = 1, ObjectTypeInformation = 2, ObjectAllTypesInformation = 3, ObjectHandleInformation = 4 } [Flags] public enum ProcessAccessFlags : uint { All = 0x001F0FFF, Terminate = 0x00000001, CreateThread = 0x00000002, VMOperation = 0x00000008, VMRead = 0x00000010, VMWrite = 0x00000020, DupHandle = 0x00000040, SetInformation = 0x00000200, QueryInformation = 0x00000400, Synchronize = 0x00100000 } [StructLayout(LayoutKind.Sequential)] public struct OBJECT_BASIC_INFORMATION { // Information Class 0 public int Attributes; public int GrantedAccess; public int HandleCount; public int PointerCount; public int PagedPoolUsage; public int NonPagedPoolUsage; public int Reserved1; public int Reserved2; public int Reserved3; public int NameInformationLength; public int TypeInformationLength; public int SecurityDescriptorLength; public System.Runtime.InteropServices.ComTypes.FILETIME CreateTime; } [StructLayout(LayoutKind.Sequential)] public struct OBJECT_TYPE_INFORMATION { // Information Class 2 public UNICODE_STRING Name; public int ObjectCount; public int HandleCount; public int Reserved1; public int Reserved2; public int Reserved3; public int Reserved4; public int PeakObjectCount; public int PeakHandleCount; public int Reserved5; public int Reserved6; public int Reserved7; public int Reserved8; public int InvalidAttributes; public GENERIC_MAPPING GenericMapping; public int ValidAccess; public byte Unknown; public byte MaintainHandleDatabase; public int PoolType; public int PagedPoolUsage; public int NonPagedPoolUsage; } [StructLayout(LayoutKind.Sequential)] public struct OBJECT_NAME_INFORMATION { // Information Class 1 public UNICODE_STRING Name; } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct UNICODE_STRING { public ushort Length; public ushort MaximumLength; public IntPtr Buffer; } [StructLayout(LayoutKind.Sequential)] public struct GENERIC_MAPPING { public int GenericRead; public int GenericWrite; public int GenericExecute; public int GenericAll; } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct SYSTEM_HANDLE_INFORMATION { // Information Class 16 public int ProcessID; public byte ObjectTypeNumber; public byte Flags; // 0x01 = PROTECT_FROM_CLOSE, 0x02 = INHERIT public ushort Handle; public int Object_Pointer; public UInt32 GrantedAccess; } public const int MAX_PATH = 260; public const uint STATUS_INFO_LENGTH_MISMATCH = 0xC0000004; public const int DUPLICATE_SAME_ACCESS = 0x2; public const int DUPLICATE_CLOSE_SOURCE = 0x1; } public class Win32Processes { const int CNST_SYSTEM_HANDLE_INFORMATION = 16; const uint STATUS_INFO_LENGTH_MISMATCH = 0xc0000004; public static string getObjectTypeName(Win32API.SYSTEM_HANDLE_INFORMATION shHandle, Process process) { IntPtr m_ipProcessHwnd = Win32API.OpenProcess(Win32API.ProcessAccessFlags.All, false, process.Id); IntPtr ipHandle = IntPtr.Zero; var objBasic = new Win32API.OBJECT_BASIC_INFORMATION(); IntPtr ipBasic = IntPtr.Zero; var objObjectType = new Win32API.OBJECT_TYPE_INFORMATION(); IntPtr ipObjectType = IntPtr.Zero; IntPtr ipObjectName = IntPtr.Zero; string strObjectTypeName = ""; int nLength = 0; int nReturn = 0; IntPtr ipTemp = IntPtr.Zero; if (!Win32API.DuplicateHandle(m_ipProcessHwnd, shHandle.Handle, Win32API.GetCurrentProcess(), out ipHandle, 0, false, Win32API.DUPLICATE_SAME_ACCESS)) return null; ipBasic = Marshal.AllocHGlobal(Marshal.SizeOf(objBasic)); Win32API.NtQueryObject(ipHandle, (int)Win32API.ObjectInformationClass.ObjectBasicInformation, ipBasic, Marshal.SizeOf(objBasic), ref nLength); objBasic = (Win32API.OBJECT_BASIC_INFORMATION)Marshal.PtrToStructure(ipBasic, objBasic.GetType()); Marshal.FreeHGlobal(ipBasic); ipObjectType = Marshal.AllocHGlobal(objBasic.TypeInformationLength); nLength = objBasic.TypeInformationLength; while ((uint)(nReturn = Win32API.NtQueryObject( ipHandle, (int)Win32API.ObjectInformationClass.ObjectTypeInformation, ipObjectType, nLength, ref nLength)) == Win32API.STATUS_INFO_LENGTH_MISMATCH) { Marshal.FreeHGlobal(ipObjectType); ipObjectType = Marshal.AllocHGlobal(nLength); } objObjectType = (Win32API.OBJECT_TYPE_INFORMATION)Marshal.PtrToStructure(ipObjectType, objObjectType.GetType()); if (Is64Bits()) { ipTemp = new IntPtr(Convert.ToInt64(objObjectType.Name.Buffer.ToString(), 10) >> 32); } else { ipTemp = objObjectType.Name.Buffer; } strObjectTypeName = Marshal.PtrToStringUni(ipTemp, objObjectType.Name.Length >> 1); Marshal.FreeHGlobal(ipObjectType); return strObjectTypeName; } public static string getObjectName(Win32API.SYSTEM_HANDLE_INFORMATION shHandle, Process process) { IntPtr m_ipProcessHwnd = Win32API.OpenProcess(Win32API.ProcessAccessFlags.All, false, process.Id); IntPtr ipHandle = IntPtr.Zero; var objBasic = new Win32API.OBJECT_BASIC_INFORMATION(); IntPtr ipBasic = IntPtr.Zero; IntPtr ipObjectType = IntPtr.Zero; var objObjectName = new Win32API.OBJECT_NAME_INFORMATION(); IntPtr ipObjectName = IntPtr.Zero; string strObjectName = ""; int nLength = 0; int nReturn = 0; IntPtr ipTemp = IntPtr.Zero; if (!Win32API.DuplicateHandle(m_ipProcessHwnd, shHandle.Handle, Win32API.GetCurrentProcess(), out ipHandle, 0, false, Win32API.DUPLICATE_SAME_ACCESS)) return null; ipBasic = Marshal.AllocHGlobal(Marshal.SizeOf(objBasic)); Win32API.NtQueryObject(ipHandle, (int)Win32API.ObjectInformationClass.ObjectBasicInformation, ipBasic, Marshal.SizeOf(objBasic), ref nLength); objBasic = (Win32API.OBJECT_BASIC_INFORMATION)Marshal.PtrToStructure(ipBasic, objBasic.GetType()); Marshal.FreeHGlobal(ipBasic); nLength = objBasic.NameInformationLength; ipObjectName = Marshal.AllocHGlobal(nLength); while ((uint)(nReturn = Win32API.NtQueryObject( ipHandle, (int)Win32API.ObjectInformationClass.ObjectNameInformation, ipObjectName, nLength, ref nLength)) == Win32API.STATUS_INFO_LENGTH_MISMATCH) { Marshal.FreeHGlobal(ipObjectName); ipObjectName = Marshal.AllocHGlobal(nLength); } objObjectName = (Win32API.OBJECT_NAME_INFORMATION)Marshal.PtrToStructure(ipObjectName, objObjectName.GetType()); if (Is64Bits()) { ipTemp = new IntPtr(Convert.ToInt64(objObjectName.Name.Buffer.ToString(), 10) >> 32); } else { ipTemp = objObjectName.Name.Buffer; } if (ipTemp != IntPtr.Zero) { byte[] baTemp2 = new byte[nLength]; try { Marshal.Copy(ipTemp, baTemp2, 0, nLength); strObjectName = Marshal.PtrToStringUni(Is64Bits() ? new IntPtr(ipTemp.ToInt64()) : new IntPtr(ipTemp.ToInt32())); return strObjectName; } catch (AccessViolationException) { return null; } finally { Marshal.FreeHGlobal(ipObjectName); Win32API.CloseHandle(ipHandle); } } return null; } public static List<Win32API.SYSTEM_HANDLE_INFORMATION> GetHandles(Process process = null, string IN_strObjectTypeName = null, string IN_strObjectName = null) { uint nStatus; int nHandleInfoSize = 0x10000; IntPtr ipHandlePointer = Marshal.AllocHGlobal(nHandleInfoSize); int nLength = 0; IntPtr ipHandle = IntPtr.Zero; while ((nStatus = Win32API.NtQuerySystemInformation(CNST_SYSTEM_HANDLE_INFORMATION, ipHandlePointer, nHandleInfoSize, ref nLength)) == STATUS_INFO_LENGTH_MISMATCH) { nHandleInfoSize = nLength; Marshal.FreeHGlobal(ipHandlePointer); ipHandlePointer = Marshal.AllocHGlobal(nLength); } byte[] baTemp = new byte[nLength]; Marshal.Copy(ipHandlePointer, baTemp, 0, nLength); long lHandleCount = 0; if (Is64Bits()) { lHandleCount = Marshal.ReadInt64(ipHandlePointer); ipHandle = new IntPtr(ipHandlePointer.ToInt64() + 8); } else { lHandleCount = Marshal.ReadInt32(ipHandlePointer); ipHandle = new IntPtr(ipHandlePointer.ToInt32() + 4); } Win32API.SYSTEM_HANDLE_INFORMATION shHandle; List<Win32API.SYSTEM_HANDLE_INFORMATION> lstHandles = new List<Win32API.SYSTEM_HANDLE_INFORMATION>(); for (long lIndex = 0; lIndex < lHandleCount; lIndex++) { shHandle = new Win32API.SYSTEM_HANDLE_INFORMATION(); if (Is64Bits()) { shHandle = (Win32API.SYSTEM_HANDLE_INFORMATION)Marshal.PtrToStructure(ipHandle, shHandle.GetType()); ipHandle = new IntPtr(ipHandle.ToInt64() + Marshal.SizeOf(shHandle) + 8); } else { ipHandle = new IntPtr(ipHandle.ToInt64() + Marshal.SizeOf(shHandle)); shHandle = (Win32API.SYSTEM_HANDLE_INFORMATION)Marshal.PtrToStructure(ipHandle, shHandle.GetType()); } if (process != null) { if (shHandle.ProcessID != process.Id) continue; } string strObjectTypeName = ""; if (IN_strObjectTypeName != null){ strObjectTypeName = getObjectTypeName(shHandle, Process.GetProcessById(shHandle.ProcessID)); if (strObjectTypeName != IN_strObjectTypeName) continue; } string strObjectName = ""; if (IN_strObjectName != null){ strObjectName = getObjectName(shHandle, Process.GetProcessById(shHandle.ProcessID)); if (strObjectName != IN_strObjectName) continue; } string strObjectTypeName2 = getObjectTypeName(shHandle, Process.GetProcessById(shHandle.ProcessID)); string strObjectName2 = getObjectName(shHandle, Process.GetProcessById(shHandle.ProcessID)); Console.WriteLine("{0} {1} {2}", shHandle.ProcessID, strObjectTypeName2, strObjectName2); lstHandles.Add(shHandle); } return lstHandles; } public static bool Is64Bits() { return Marshal.SizeOf(typeof(IntPtr)) == 8 ? true : false; } } class Program { static void Main(string[] args) { String MutexName = "MSCTF.Asm.MutexDefault1"; String ProcessName = "notepad"; try { Process process = Process.GetProcessesByName(ProcessName)[0]; var handles = Win32Processes.GetHandles(process, "Mutant", "\\Sessions\\1\\BaseNamedObjects\\" + MutexName); if (handles.Count == 0) throw new System.ArgumentException("NoMutex", "original"); foreach (var handle in handles) { IntPtr ipHandle = IntPtr.Zero; if (!Win32API.DuplicateHandle(Process.GetProcessById(handle.ProcessID).Handle, handle.Handle, Win32API.GetCurrentProcess(), out ipHandle, 0, false, Win32API.DUPLICATE_CLOSE_SOURCE)) Console.WriteLine("DuplicateHandle() failed, error = {0}", Marshal.GetLastWin32Error()); Console.WriteLine("Mutex was killed"); } } catch (IndexOutOfRangeException) { Console.WriteLine("The process name '{0}' is not currently running", ProcessName); } catch (ArgumentException) { Console.WriteLine("The Mutex '{0}' was not found in the process '{1}'", MutexName, ProcessName); } Console.ReadLine(); } } }
You need elevated privileges for that - especially DEBUG privilege. See: Manipulate Privileges in Managed Code Reliably, Securely, and Efficiently (MSDN) Debug Privilege (MSDN) The Windows Access Control Model Part 3 Simon Mourier's answer to Stack Overflow question How to enable the SeCreateGlobalPrivilege in .NET without resorting to P/Invoke or reflection? ObjectSecurity Methods (MSDN)
IExtractImage works from a console application but not a plug-in?
I'm writing a program that needs to be able to extract the thumbnail image from a file. I've gotten a hold of a class, ThumbnailCreator, which is able to do this. The source of this Class is below. using System; using System.Drawing; using System.Runtime.InteropServices; using System.Text; using System.IO; using System.Drawing.Imaging; internal class ThumbnailCreator : IDisposable { // Fields private IMalloc alloc; private Size desiredSize; private bool disposed; private static readonly string fileExtention = ".jpg"; private Bitmap thumbnail; // Methods public ThumbnailCreator() { this.desiredSize = new Size(100, 100); } public ThumbnailCreator(Size desiredSize) { this.desiredSize = new Size(100, 100); this.DesiredSize = desiredSize; } public void Dispose() { if (!this.disposed) { if (this.alloc != null) { Marshal.ReleaseComObject(this.alloc); } this.alloc = null; if (this.thumbnail != null) { this.thumbnail.Dispose(); } this.disposed = true; } } ~ThumbnailCreator() { this.Dispose(); } private bool getThumbNail(string file, IntPtr pidl, IShellFolder item) { bool CS; IntPtr hBmp = IntPtr.Zero; IExtractImage extractImage = null; try { if (Path.GetFileName(PathFromPidl(pidl)).ToUpper().Equals(Path.GetFileName(file).ToUpper())) { int prgf; IUnknown iunk = null; Guid iidExtractImage = new Guid("BB2E617C-0920-11d1-9A0B-00C04FC2D6C1"); item.GetUIObjectOf(IntPtr.Zero, 1, ref pidl, ref iidExtractImage, out prgf, ref iunk); extractImage = (IExtractImage) iunk; if (extractImage != null) { SIZE sz = new SIZE { cx = this.desiredSize.Width, cy = this.desiredSize.Height }; StringBuilder location = new StringBuilder(260, 260); int priority = 0; int requestedColourDepth = 0x20; EIEIFLAG flags = EIEIFLAG.IEIFLAG_SCREEN | EIEIFLAG.IEIFLAG_ASPECT; int uFlags = (int) flags; extractImage.GetLocation(location, location.Capacity, ref priority, ref sz, requestedColourDepth, ref uFlags); extractImage.Extract(out hBmp); if (hBmp != IntPtr.Zero) { this.thumbnail = Image.FromHbitmap(hBmp); } Marshal.ReleaseComObject(extractImage); extractImage = null; } return true; } CS = false; } catch (Exception) { if (hBmp != IntPtr.Zero) { UnManagedMethods.DeleteObject(hBmp); } if (extractImage != null) { Marshal.ReleaseComObject(extractImage); } throw; } return CS; } public Bitmap GetThumbNail(string file) { if (!File.Exists(file) && !Directory.Exists(file)) { throw new FileNotFoundException(string.Format("The file '{0}' does not exist", file), file); } if (this.thumbnail != null) { this.thumbnail.Dispose(); this.thumbnail = null; } IShellFolder folder = getDesktopFolder; if (folder != null) { IntPtr pidlMain; try { int cParsed; int pdwAttrib; string filePath = Path.GetDirectoryName(file); folder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, filePath, out cParsed, out pidlMain, out pdwAttrib); } catch (Exception) { Marshal.ReleaseComObject(folder); throw; } if (pidlMain != IntPtr.Zero) { Guid iidShellFolder = new Guid("000214E6-0000-0000-C000-000000000046"); IShellFolder item = null; try { folder.BindToObject(pidlMain, IntPtr.Zero, ref iidShellFolder, ref item); } catch (Exception) { Marshal.ReleaseComObject(folder); this.Allocator.Free(pidlMain); throw; } if (item != null) { IEnumIDList idEnum = null; try { item.EnumObjects(IntPtr.Zero, ESHCONTF.SHCONTF_NONFOLDERS | ESHCONTF.SHCONTF_FOLDERS, ref idEnum); } catch (Exception) { Marshal.ReleaseComObject(folder); this.Allocator.Free(pidlMain); throw; } if (idEnum != null) { IntPtr pidl = IntPtr.Zero; bool complete = false; while (!complete) { int fetched; if (idEnum.Next(1, ref pidl, out fetched) != 0) { pidl = IntPtr.Zero; complete = true; } else if (this.getThumbNail(file, pidl, item)) { complete = true; } if (pidl != IntPtr.Zero) { this.Allocator.Free(pidl); } } Marshal.ReleaseComObject(idEnum); } Marshal.ReleaseComObject(item); } this.Allocator.Free(pidlMain); } Marshal.ReleaseComObject(folder); } return this.thumbnail; } private static string PathFromPidl(IntPtr pidl) { StringBuilder path = new StringBuilder(260, 260); if (UnManagedMethods.SHGetPathFromIDList(pidl, path) != 0) { return path.ToString(); } return string.Empty; } // Properties private IMalloc Allocator { get { if (!this.disposed && (this.alloc == null)) { UnManagedMethods.SHGetMalloc(out this.alloc); } return this.alloc; } } public Size DesiredSize { get { return this.desiredSize; } set { this.desiredSize = value; } } private static IShellFolder getDesktopFolder { get { IShellFolder ppshf; UnManagedMethods.SHGetDesktopFolder(out ppshf); return ppshf; } } public Bitmap ThumbNail { get { return this.thumbnail; } } // Nested Types private enum EIEIFLAG { IEIFLAG_ASPECT = 4, IEIFLAG_ASYNC = 1, IEIFLAG_CACHE = 2, IEIFLAG_GLEAM = 0x10, IEIFLAG_NOBORDER = 0x100, IEIFLAG_NOSTAMP = 0x80, IEIFLAG_OFFLINE = 8, IEIFLAG_ORIGSIZE = 0x40, IEIFLAG_QUALITY = 0x200, IEIFLAG_SCREEN = 0x20 } [Flags] private enum ESFGAO { SFGAO_CANCOPY = 1, SFGAO_CANDELETE = 0x20, SFGAO_CANLINK = 4, SFGAO_CANMOVE = 2, SFGAO_CANRENAME = 0x10, SFGAO_CAPABILITYMASK = 0x177, SFGAO_COMPRESSED = 0x4000000, SFGAO_CONTENTSMASK = -2147483648, SFGAO_DISPLAYATTRMASK = 0xf0000, SFGAO_DROPTARGET = 0x100, SFGAO_FILESYSANCESTOR = 0x10000000, SFGAO_FILESYSTEM = 0x40000000, SFGAO_FOLDER = 0x20000000, SFGAO_GHOSTED = 0x80000, SFGAO_HASPROPSHEET = 0x40, SFGAO_HASSUBFOLDER = -2147483648, SFGAO_LINK = 0x10000, SFGAO_READONLY = 0x40000, SFGAO_REMOVABLE = 0x2000000, SFGAO_SHARE = 0x20000, SFGAO_VALIDATE = 0x1000000 } [Flags] private enum ESHCONTF { SHCONTF_FOLDERS = 0x20, SHCONTF_INCLUDEHIDDEN = 0x80, SHCONTF_NONFOLDERS = 0x40 } [Flags] private enum ESHGDN { SHGDN_FORADDRESSBAR = 0x4000, SHGDN_FORPARSING = 0x8000, SHGDN_INFOLDER = 1, SHGDN_NORMAL = 0 } [Flags] private enum ESTRRET { STRRET_WSTR, STRRET_OFFSET, STRRET_CSTR } [ComImport, Guid("000214F2-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IEnumIDList { [PreserveSig] int Next(int celt, ref IntPtr rgelt, out int pceltFetched); void Skip(int celt); void Reset(); void Clone(ref ThumbnailCreator.IEnumIDList ppenum); } [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("BB2E617C-0920-11d1-9A0B-00C04FC2D6C1")] private interface IExtractImage { void GetLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszPathBuffer, int cch, ref int pdwPriority, ref ThumbnailCreator.SIZE prgSize, int dwRecClrDepth, ref int pdwFlags); void Extract(out IntPtr phBmpThumbnail); } [ComImport, Guid("00000002-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IMalloc { [PreserveSig] IntPtr Alloc(int cb); [PreserveSig] IntPtr Realloc(IntPtr pv, int cb); [PreserveSig] void Free(IntPtr pv); [PreserveSig] int GetSize(IntPtr pv); [PreserveSig] int DidAlloc(IntPtr pv); [PreserveSig] void HeapMinimize(); } [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214E6-0000-0000-C000-000000000046")] private interface IShellFolder { void ParseDisplayName(IntPtr hwndOwner, IntPtr pbcReserved, [MarshalAs(UnmanagedType.LPWStr)] string lpszDisplayName, out int pchEaten, out IntPtr ppidl, out int pdwAttributes); void EnumObjects(IntPtr hwndOwner, [MarshalAs(UnmanagedType.U4)] ThumbnailCreator.ESHCONTF grfFlags, ref ThumbnailCreator.IEnumIDList ppenumIDList); void BindToObject(IntPtr pidl, IntPtr pbcReserved, ref Guid riid, ref ThumbnailCreator.IShellFolder ppvOut); void BindToStorage(IntPtr pidl, IntPtr pbcReserved, ref Guid riid, IntPtr ppvObj); [PreserveSig] int CompareIDs(IntPtr lParam, IntPtr pidl1, IntPtr pidl2); void CreateViewObject(IntPtr hwndOwner, ref Guid riid, IntPtr ppvOut); void GetAttributesOf(int cidl, IntPtr apidl, [MarshalAs(UnmanagedType.U4)] ref ThumbnailCreator.ESFGAO rgfInOut); void GetUIObjectOf(IntPtr hwndOwner, int cidl, ref IntPtr apidl, ref Guid riid, out int prgfInOut, ref ThumbnailCreator.IUnknown ppvOut); void GetDisplayNameOf(IntPtr pidl, [MarshalAs(UnmanagedType.U4)] ThumbnailCreator.ESHGDN uFlags, ref ThumbnailCreator.STRRET_CSTR lpName); void SetNameOf(IntPtr hwndOwner, IntPtr pidl, [MarshalAs(UnmanagedType.LPWStr)] string lpszName, [MarshalAs(UnmanagedType.U4)] ThumbnailCreator.ESHCONTF uFlags, ref IntPtr ppidlOut); } [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("00000000-0000-0000-C000-000000000046")] private interface IUnknown { [PreserveSig] IntPtr QueryInterface(ref Guid riid, out IntPtr pVoid); [PreserveSig] IntPtr AddRef(); [PreserveSig] IntPtr Release(); } [StructLayout(LayoutKind.Sequential)] private struct SIZE { public int cx; public int cy; } [StructLayout(LayoutKind.Explicit, CharSet=CharSet.Auto)] private struct STRRET_ANY { // Fields [FieldOffset(4)] public IntPtr pOLEString; [FieldOffset(0)] public ThumbnailCreator.ESTRRET uType; } [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto, Pack=4)] private struct STRRET_CSTR { public ThumbnailCreator.ESTRRET uType; [MarshalAs(UnmanagedType.ByValArray, SizeConst=520)] public byte[] cStr; } private class UnManagedMethods { // Methods [DllImport("gdi32", CharSet=CharSet.Auto)] internal static extern int DeleteObject(IntPtr hObject); [DllImport("shell32", CharSet=CharSet.Auto)] internal static extern int SHGetDesktopFolder(out ThumbnailCreator.IShellFolder ppshf); [DllImport("shell32", CharSet=CharSet.Auto)] internal static extern int SHGetMalloc(out ThumbnailCreator.IMalloc ppMalloc); [DllImport("shell32", CharSet=CharSet.Auto)] internal static extern int SHGetPathFromIDList(IntPtr pidl, StringBuilder pszPath); } } As a test I created a quick console application, the code of which is below. This test worked fine and was able to extract a thumbnail, and saved it to a PNG file. static void Main(string[] args) { try { Console.WriteLine("press a key to extract"); System.Console.ReadKey(); string path = #"C:\somefile.xyz"; ThumbnailCreator creator = new ThumbnailCreator(); creator.DesiredSize = new Size(600, 600); Bitmap bm = creator.GetThumbNail(path); bm.Save(#"C:\blah.png", System.Drawing.Imaging.ImageFormat.Png); Console.WriteLine("press a key to exit"); System.Console.ReadKey(); } catch (Exception exp) { Console.WriteLine(exp.Message); } } My problem is the real application I want to use this in runs as a plug-in for another application. When I try to run the same test code in the plug-in creator.GetThumbNail(path); returns null. I debugged a little further and found that in the method ThumbnailCreator.getThumbNail(string file, IntPtr pidl,IshellFolder item) the line extractImage.Extract(out hBmp); returns IntPtr.Zero. Whereas in the console application that works this method actually returns a number. Maybe this is the problem? Or maybe this problem happens before this. Honestly, I'm completely lost when it comes to Interop and Windows API stuff. Does anyone know of any possible reasons why this class would work in a standalone console application, but not as part of a plug-in for another application? Update Here's a bit more detail of how this plug-in is created. To add a custom command to this program you create a class that implements an ICommand interface from is API. The interface has a single Execute() method where code is place that should run when the user executes the custom command from the program. The native application, however, is what actualy instantiates my command class and calls the execute method. What could be different about the way that the native app instantiates my command class and or calls the Execute() method that would prevent the thumbnail extraction from working?
Just ran across this issue myself and I do say, it took some time to figure out that it was a bitness issue. In your scenario your console app was probably compiled as x64 and your Windows app as x86. The ExtractImage interface (BB2E617C-0920-11d1-9A0B-00C04FC2D6C1) on a 64bit OS is only accessible from 64bit applications (because explorer is 64bit), thus resulting in a "Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG))" exception in applications compiled as x86.
Code that calls the Shell API must be in a COM Single Threaded Apartment, try putting [STAThread] attribute on the thread that calls this.
How do I find out which process is locking a file using .NET?
I've seen several of answers about using Handle or Process Monitor, but I would like to be able to find out in my own code (C#) which process is locking a file. I have a nasty feeling that I'm going to have to spelunk around in the win32 API, but if anyone has already done this and can put me on the right track, I'd really appreciate the help. Update Links to similar questions How does one figure out what process locked a file using c#? Command line tool Across a Network Locking a USB device Unit test fails with locked file deleting locked file
Long ago it was impossible to reliably get the list of processes locking a file because Windows simply did not track that information. To support the Restart Manager API, that information is now tracked. I put together code that takes the path of a file and returns a List<Process> of all processes that are locking that file. using System.Runtime.InteropServices; using System.Diagnostics; using System; using System.Collections.Generic; static public class FileUtil { [StructLayout(LayoutKind.Sequential)] 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; enum RM_APP_TYPE { RmUnknownApp = 0, RmMainWindow = 1, RmOtherWindow = 2, RmService = 3, RmExplorer = 4, RmConsole = 5, RmCritical = 1000 } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 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, UInt32 nFiles, string[] rgsFilenames, UInt32 nApplications, [In] RM_UNIQUE_PROCESS[] rgApplications, UInt32 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); /// <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> static public List<Process> WhoIsLocking(string path) { uint handle; string key = Guid.NewGuid().ToString(); List<Process> processes = new List<Process>(); 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 ERROR_MORE_DATA = 234; uint pnProcInfoNeeded = 0, pnProcInfo = 0, lpdwRebootReasons = RmRebootReasonNone; string[] resources = new string[] { 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 == ERROR_MORE_DATA) { // Create an array to store the process results RM_PROCESS_INFO[] processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded]; pnProcInfo = pnProcInfoNeeded; // Get the list res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons); if (res == 0) { processes = new List<Process>((int)pnProcInfo); // Enumerate all of the results and add them to the // list to be returned for (int i = 0; i < pnProcInfo; i++) { try { processes.Add(Process.GetProcessById(processInfo[i].Process.dwProcessId)); } // catch the error -- in case the process is no longer running catch (ArgumentException) { } } } else throw new Exception("Could not list processes locking resource."); } else if (res != 0) throw new Exception("Could not list processes locking resource. Failed to get size of result."); } finally { RmEndSession(handle); } return processes; } } Using from Limited Permission (e.g. IIS) This call accesses the registry. If the process does not have permission to do so, you will get ERROR_WRITE_FAULT, meaning An operation was unable to read or write to the registry. You could selectively grant permission to your restricted account to the necessary part of the registry. It is more secure though to have your limited access process set a flag (e.g. in the database or the file system, or by using an interprocess communication mechanism such as queue or named pipe) and have a second process call the Restart Manager API. Granting other-than-minimal permissions to the IIS user is a security risk.
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; } } }
It is very complex to invoke Win32 from C#. You should use the tool Handle.exe. After that your C# code have to be the following: string fileName = #"c:\aaa.doc";//Path to locked file Process tool = new Process(); tool.StartInfo.FileName = "handle.exe"; tool.StartInfo.Arguments = fileName+" /accepteula"; tool.StartInfo.UseShellExecute = false; tool.StartInfo.RedirectStandardOutput = true; tool.Start(); tool.WaitForExit(); string outputTool = tool.StandardOutput.ReadToEnd(); string matchPattern = #"(?<=\s+pid:\s+)\b(\d+)\b(?=\s+)"; foreach(Match match in Regex.Matches(outputTool, matchPattern)) { Process.GetProcessById(int.Parse(match.Value)).Kill(); }
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.
The code I found here, https://vmccontroller.svn.codeplex.com/svn/VmcController/VmcServices/DetectOpenFiles.cs Works for me much better than the code provided by Iain. Iain's code seemed to be acquiring a lock of its own. Here is my slightly modified version of the code above modified to return the string path of the files locked instead of the FileSystemInfo object, using System; using System.Collections.Generic; //using System.EnterpriseServices; using System.IO; using System.Runtime.CompilerServices; using System.Runtime.ConstrainedExecution; using System.Runtime.InteropServices; using System.Security.Permissions; using System.Text; using System.Threading; using Microsoft.Win32.SafeHandles; namespace Crmc.Core.BuildTasks { using System.Diagnostics; using System.Linq; #region ENUMs internal enum NT_STATUS { STATUS_SUCCESS = 0x00000000, STATUS_BUFFER_OVERFLOW = unchecked((int)0x80000005L), STATUS_INFO_LENGTH_MISMATCH = unchecked((int)0xC0000004L) } internal enum SYSTEM_INFORMATION_CLASS { SystemBasicInformation = 0, SystemPerformanceInformation = 2, SystemTimeOfDayInformation = 3, SystemProcessInformation = 5, SystemProcessorPerformanceInformation = 8, SystemHandleInformation = 16, SystemInterruptInformation = 23, SystemExceptionInformation = 33, SystemRegistryQuotaInformation = 37, SystemLookasideInformation = 45 } internal enum OBJECT_INFORMATION_CLASS { ObjectBasicInformation = 0, ObjectNameInformation = 1, ObjectTypeInformation = 2, ObjectAllTypesInformation = 3, ObjectHandleInformation = 4 } [Flags] internal enum ProcessAccessRights { PROCESS_DUP_HANDLE = 0x00000040 } [Flags] internal enum DuplicateHandleOptions { DUPLICATE_CLOSE_SOURCE = 0x1, DUPLICATE_SAME_ACCESS = 0x2 } #endregion [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)] internal sealed class SafeObjectHandle : SafeHandleZeroOrMinusOneIsInvalid { private SafeObjectHandle() : base(true) { } internal SafeObjectHandle(IntPtr preexistingHandle, bool ownsHandle) : base(ownsHandle) { base.SetHandle(preexistingHandle); } protected override bool ReleaseHandle() { return NativeMethods.CloseHandle(base.handle); } } [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)] internal sealed class SafeProcessHandle : SafeHandleZeroOrMinusOneIsInvalid { private SafeProcessHandle() : base(true) { } internal SafeProcessHandle(IntPtr preexistingHandle, bool ownsHandle) : base(ownsHandle) { base.SetHandle(preexistingHandle); } protected override bool ReleaseHandle() { return NativeMethods.CloseHandle(base.handle); } } #region Native Methods internal static class NativeMethods { [DllImport("ntdll.dll")] internal static extern NT_STATUS NtQuerySystemInformation( [In] SYSTEM_INFORMATION_CLASS SystemInformationClass, [In] IntPtr SystemInformation, [In] int SystemInformationLength, [Out] out int ReturnLength); [DllImport("ntdll.dll")] internal static extern NT_STATUS NtQueryObject( [In] IntPtr Handle, [In] OBJECT_INFORMATION_CLASS ObjectInformationClass, [In] IntPtr ObjectInformation, [In] int ObjectInformationLength, [Out] out int ReturnLength); [DllImport("kernel32.dll", SetLastError = true)] internal static extern SafeProcessHandle OpenProcess( [In] ProcessAccessRights dwDesiredAccess, [In, MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, [In] int dwProcessId); [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool DuplicateHandle( [In] IntPtr hSourceProcessHandle, [In] IntPtr hSourceHandle, [In] IntPtr hTargetProcessHandle, [Out] out SafeObjectHandle lpTargetHandle, [In] int dwDesiredAccess, [In, MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, [In] DuplicateHandleOptions dwOptions); [DllImport("kernel32.dll")] internal static extern IntPtr GetCurrentProcess(); [DllImport("kernel32.dll", SetLastError = true)] internal static extern int GetProcessId( [In] IntPtr Process); [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool CloseHandle( [In] IntPtr hObject); [DllImport("kernel32.dll", SetLastError = true)] internal static extern int QueryDosDevice( [In] string lpDeviceName, [Out] StringBuilder lpTargetPath, [In] int ucchMax); } #endregion //[ComVisible(true), EventTrackingEnabled(true)] public class DetectOpenFiles// : ServicedComponent { private static Dictionary<string, string> deviceMap; private const string networkDevicePrefix = "\\Device\\LanmanRedirector\\"; private const int MAX_PATH = 260; private enum SystemHandleType { OB_TYPE_UNKNOWN = 0, OB_TYPE_TYPE = 1, OB_TYPE_DIRECTORY, OB_TYPE_SYMBOLIC_LINK, OB_TYPE_TOKEN, OB_TYPE_PROCESS, OB_TYPE_THREAD, OB_TYPE_UNKNOWN_7, OB_TYPE_EVENT, OB_TYPE_EVENT_PAIR, OB_TYPE_MUTANT, OB_TYPE_UNKNOWN_11, OB_TYPE_SEMAPHORE, OB_TYPE_TIMER, OB_TYPE_PROFILE, OB_TYPE_WINDOW_STATION, OB_TYPE_DESKTOP, OB_TYPE_SECTION, OB_TYPE_KEY, OB_TYPE_PORT, OB_TYPE_WAITABLE_PORT, OB_TYPE_UNKNOWN_21, OB_TYPE_UNKNOWN_22, OB_TYPE_UNKNOWN_23, OB_TYPE_UNKNOWN_24, //OB_TYPE_CONTROLLER, //OB_TYPE_DEVICE, //OB_TYPE_DRIVER, OB_TYPE_IO_COMPLETION, OB_TYPE_FILE }; private const int handleTypeTokenCount = 27; private static readonly string[] handleTypeTokens = new string[] { "", "", "Directory", "SymbolicLink", "Token", "Process", "Thread", "Unknown7", "Event", "EventPair", "Mutant", "Unknown11", "Semaphore", "Timer", "Profile", "WindowStation", "Desktop", "Section", "Key", "Port", "WaitablePort", "Unknown21", "Unknown22", "Unknown23", "Unknown24", "IoCompletion", "File" }; [StructLayout(LayoutKind.Sequential)] private struct SYSTEM_HANDLE_ENTRY { public int OwnerPid; public byte ObjectType; public byte HandleFlags; public short HandleValue; public int ObjectPointer; public int AccessMask; } /// <summary> /// Gets the open files enumerator. /// </summary> /// <param name="processId">The process id.</param> /// <returns></returns> public static IEnumerable<String> GetOpenFilesEnumerator(int processId) { return new OpenFiles(processId); } public static List<Process> GetProcessesUsingFile(string fName) { List<Process> result = new List<Process>(); foreach (var p in Process.GetProcesses()) { try { if (DetectOpenFiles.GetOpenFilesEnumerator(p.Id).Contains(fName)) { result.Add(p); } } catch { }//some processes will fail } return result; } private sealed class OpenFiles : IEnumerable<String> { private readonly int processId; internal OpenFiles(int processId) { this.processId = processId; } #region IEnumerable<FileSystemInfo> Members public IEnumerator<String> GetEnumerator() { NT_STATUS ret; int length = 0x10000; // Loop, probing for required memory. do { IntPtr ptr = IntPtr.Zero; RuntimeHelpers.PrepareConstrainedRegions(); try { RuntimeHelpers.PrepareConstrainedRegions(); try { } finally { // CER guarantees that the address of the allocated // memory is actually assigned to ptr if an // asynchronous exception occurs. ptr = Marshal.AllocHGlobal(length); } int returnLength; ret = NativeMethods.NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS.SystemHandleInformation, ptr, length, out returnLength); if (ret == NT_STATUS.STATUS_INFO_LENGTH_MISMATCH) { // Round required memory up to the nearest 64KB boundary. length = ((returnLength + 0xffff) & ~0xffff); } else if (ret == NT_STATUS.STATUS_SUCCESS) { int handleCount = Marshal.ReadInt32(ptr); int offset = sizeof(int); int size = Marshal.SizeOf(typeof(SYSTEM_HANDLE_ENTRY)); for (int i = 0; i < handleCount; i++) { SYSTEM_HANDLE_ENTRY handleEntry = (SYSTEM_HANDLE_ENTRY)Marshal.PtrToStructure((IntPtr)((int)ptr + offset), typeof(SYSTEM_HANDLE_ENTRY)); if (handleEntry.OwnerPid == processId) { IntPtr handle = (IntPtr)handleEntry.HandleValue; SystemHandleType handleType; if (GetHandleType(handle, handleEntry.OwnerPid, out handleType) && handleType == SystemHandleType.OB_TYPE_FILE) { string devicePath; if (GetFileNameFromHandle(handle, handleEntry.OwnerPid, out devicePath)) { string dosPath; if (ConvertDevicePathToDosPath(devicePath, out dosPath)) { if (File.Exists(dosPath)) { yield return dosPath; // return new FileInfo(dosPath); } else if (Directory.Exists(dosPath)) { yield return dosPath; // new DirectoryInfo(dosPath); } } } } } offset += size; } } } finally { // CER guarantees that the allocated memory is freed, // if an asynchronous exception occurs. Marshal.FreeHGlobal(ptr); //sw.Flush(); //sw.Close(); } } while (ret == NT_STATUS.STATUS_INFO_LENGTH_MISMATCH); } #endregion #region IEnumerable Members System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion } #region Private Members private static bool GetFileNameFromHandle(IntPtr handle, int processId, out string fileName) { IntPtr currentProcess = NativeMethods.GetCurrentProcess(); bool remote = (processId != NativeMethods.GetProcessId(currentProcess)); SafeProcessHandle processHandle = null; SafeObjectHandle objectHandle = null; try { if (remote) { processHandle = NativeMethods.OpenProcess(ProcessAccessRights.PROCESS_DUP_HANDLE, true, processId); if (NativeMethods.DuplicateHandle(processHandle.DangerousGetHandle(), handle, currentProcess, out objectHandle, 0, false, DuplicateHandleOptions.DUPLICATE_SAME_ACCESS)) { handle = objectHandle.DangerousGetHandle(); } } return GetFileNameFromHandle(handle, out fileName, 200); } finally { if (remote) { if (processHandle != null) { processHandle.Close(); } if (objectHandle != null) { objectHandle.Close(); } } } } private static bool GetFileNameFromHandle(IntPtr handle, out string fileName, int wait) { using (FileNameFromHandleState f = new FileNameFromHandleState(handle)) { ThreadPool.QueueUserWorkItem(new WaitCallback(GetFileNameFromHandle), f); if (f.WaitOne(wait)) { fileName = f.FileName; return f.RetValue; } else { fileName = string.Empty; return false; } } } private class FileNameFromHandleState : IDisposable { private ManualResetEvent _mr; private IntPtr _handle; private string _fileName; private bool _retValue; public IntPtr Handle { get { return _handle; } } public string FileName { get { return _fileName; } set { _fileName = value; } } public bool RetValue { get { return _retValue; } set { _retValue = value; } } public FileNameFromHandleState(IntPtr handle) { _mr = new ManualResetEvent(false); this._handle = handle; } public bool WaitOne(int wait) { return _mr.WaitOne(wait, false); } public void Set() { try { _mr.Set(); } catch{} } #region IDisposable Members public void Dispose() { if (_mr != null) _mr.Close(); } #endregion } private static void GetFileNameFromHandle(object state) { FileNameFromHandleState s = (FileNameFromHandleState)state; string fileName; s.RetValue = GetFileNameFromHandle(s.Handle, out fileName); s.FileName = fileName; s.Set(); } private static bool GetFileNameFromHandle(IntPtr handle, out string fileName) { IntPtr ptr = IntPtr.Zero; RuntimeHelpers.PrepareConstrainedRegions(); try { int length = 0x200; // 512 bytes RuntimeHelpers.PrepareConstrainedRegions(); try { } finally { // CER guarantees the assignment of the allocated // memory address to ptr, if an ansynchronous exception // occurs. ptr = Marshal.AllocHGlobal(length); } NT_STATUS ret = NativeMethods.NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectNameInformation, ptr, length, out length); if (ret == NT_STATUS.STATUS_BUFFER_OVERFLOW) { RuntimeHelpers.PrepareConstrainedRegions(); try { } finally { // CER guarantees that the previous allocation is freed, // and that the newly allocated memory address is // assigned to ptr if an asynchronous exception occurs. Marshal.FreeHGlobal(ptr); ptr = Marshal.AllocHGlobal(length); } ret = NativeMethods.NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectNameInformation, ptr, length, out length); } if (ret == NT_STATUS.STATUS_SUCCESS) { fileName = Marshal.PtrToStringUni((IntPtr)((int)ptr + 8), (length - 9) / 2); return fileName.Length != 0; } } finally { // CER guarantees that the allocated memory is freed, // if an asynchronous exception occurs. Marshal.FreeHGlobal(ptr); } fileName = string.Empty; return false; } private static bool GetHandleType(IntPtr handle, int processId, out SystemHandleType handleType) { string token = GetHandleTypeToken(handle, processId); return GetHandleTypeFromToken(token, out handleType); } private static bool GetHandleType(IntPtr handle, out SystemHandleType handleType) { string token = GetHandleTypeToken(handle); return GetHandleTypeFromToken(token, out handleType); } private static bool GetHandleTypeFromToken(string token, out SystemHandleType handleType) { for (int i = 1; i < handleTypeTokenCount; i++) { if (handleTypeTokens[i] == token) { handleType = (SystemHandleType)i; return true; } } handleType = SystemHandleType.OB_TYPE_UNKNOWN; return false; } private static string GetHandleTypeToken(IntPtr handle, int processId) { IntPtr currentProcess = NativeMethods.GetCurrentProcess(); bool remote = (processId != NativeMethods.GetProcessId(currentProcess)); SafeProcessHandle processHandle = null; SafeObjectHandle objectHandle = null; try { if (remote) { processHandle = NativeMethods.OpenProcess(ProcessAccessRights.PROCESS_DUP_HANDLE, true, processId); if (NativeMethods.DuplicateHandle(processHandle.DangerousGetHandle(), handle, currentProcess, out objectHandle, 0, false, DuplicateHandleOptions.DUPLICATE_SAME_ACCESS)) { handle = objectHandle.DangerousGetHandle(); } } return GetHandleTypeToken(handle); } finally { if (remote) { if (processHandle != null) { processHandle.Close(); } if (objectHandle != null) { objectHandle.Close(); } } } } private static string GetHandleTypeToken(IntPtr handle) { int length; NativeMethods.NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectTypeInformation, IntPtr.Zero, 0, out length); IntPtr ptr = IntPtr.Zero; RuntimeHelpers.PrepareConstrainedRegions(); try { RuntimeHelpers.PrepareConstrainedRegions(); try { } finally { ptr = Marshal.AllocHGlobal(length); } if (NativeMethods.NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectTypeInformation, ptr, length, out length) == NT_STATUS.STATUS_SUCCESS) { return Marshal.PtrToStringUni((IntPtr)((int)ptr + 0x60)); } } finally { Marshal.FreeHGlobal(ptr); } return string.Empty; } private static bool ConvertDevicePathToDosPath(string devicePath, out string dosPath) { EnsureDeviceMap(); int i = devicePath.Length; while (i > 0 && (i = devicePath.LastIndexOf('\\', i - 1)) != -1) { string drive; if (deviceMap.TryGetValue(devicePath.Substring(0, i), out drive)) { dosPath = string.Concat(drive, devicePath.Substring(i)); return dosPath.Length != 0; } } dosPath = string.Empty; return false; } private static void EnsureDeviceMap() { if (deviceMap == null) { Dictionary<string, string> localDeviceMap = BuildDeviceMap(); Interlocked.CompareExchange<Dictionary<string, string>>(ref deviceMap, localDeviceMap, null); } } private static Dictionary<string, string> BuildDeviceMap() { string[] logicalDrives = Environment.GetLogicalDrives(); Dictionary<string, string> localDeviceMap = new Dictionary<string, string>(logicalDrives.Length); StringBuilder lpTargetPath = new StringBuilder(MAX_PATH); foreach (string drive in logicalDrives) { string lpDeviceName = drive.Substring(0, 2); NativeMethods.QueryDosDevice(lpDeviceName, lpTargetPath, MAX_PATH); localDeviceMap.Add(NormalizeDeviceName(lpTargetPath.ToString()), lpDeviceName); } localDeviceMap.Add(networkDevicePrefix.Substring(0, networkDevicePrefix.Length - 1), "\\"); return localDeviceMap; } private static string NormalizeDeviceName(string deviceName) { if (string.Compare(deviceName, 0, networkDevicePrefix, 0, networkDevicePrefix.Length, StringComparison.InvariantCulture) == 0) { string shareName = deviceName.Substring(deviceName.IndexOf('\\', networkDevicePrefix.Length) + 1); return string.Concat(networkDevicePrefix, shareName); } return deviceName; } #endregion } }
Not very straightforward, but on Windows Vista and above you can use the Restart Manager APIs to see who is using a file. Internet Explorer caches settings includes details on using this to detect which process has iexplore.exe open. Omitting a lot of detail: // Start an RM session RmStartSession(&sessionHandle, 0, sessionKey); // Register the file you are checking RmRegisterResources(sessionHandle, 1, filePathArray, 0, NULL, 0, NULL); // Get all processes that have that file open. RmGetList(sessionHAndle, &nProcInfoNeeded, &nProcInfo, processes, &rebootReason); RmEndSession(sessionHandle);
Handle, from Windows Sysinternals. This is a free command-line utility provided by Microsoft. You could run it, and parse the result.
I had issues with stefan's solution. Below is a modified version which seems to work well. using System; using System.Collections; using System.Diagnostics; using System.Management; using System.IO; static class Module1 { static internal ArrayList myProcessArray = new ArrayList(); private static Process myProcess; public static void Main() { string strFile = "c:\\windows\\system32\\msi.dll"; ArrayList a = getFileProcesses(strFile); foreach (Process p in a) { Debug.Print(p.ProcessName); } } private static ArrayList getFileProcesses(string strFile) { myProcessArray.Clear(); Process[] processes = Process.GetProcesses(); int i = 0; for (i = 0; i <= processes.GetUpperBound(0) - 1; i++) { myProcess = processes[i]; //if (!myProcess.HasExited) //This will cause an "Access is denied" error if (myProcess.Threads.Count > 0) { try { ProcessModuleCollection modules = myProcess.Modules; int j = 0; for (j = 0; j <= modules.Count - 1; j++) { if ((modules[j].FileName.ToLower().CompareTo(strFile.ToLower()) == 0)) { myProcessArray.Add(myProcess); break; // TODO: might not be correct. Was : Exit For } } } catch (Exception exception) { //MsgBox(("Error : " & exception.Message)) } } } return myProcessArray; } } UPDATE If you just want to know which process(es) are locking a particular DLL, you can execute and parse the output of tasklist /m YourDllName.dll. Works on Windows XP and later. See What does this do? tasklist /m "mscor*"
This works for DLLs locked by other processes. This routine will not find out for example that a text file is locked by a word process. C#: using System.Management; using System.IO; static class Module1 { static internal ArrayList myProcessArray = new ArrayList(); private static Process myProcess; public static void Main() { string strFile = "c:\\windows\\system32\\msi.dll"; ArrayList a = getFileProcesses(strFile); foreach (Process p in a) { Debug.Print(p.ProcessName); } } private static ArrayList getFileProcesses(string strFile) { myProcessArray.Clear(); Process[] processes = Process.GetProcesses; int i = 0; for (i = 0; i <= processes.GetUpperBound(0) - 1; i++) { myProcess = processes(i); if (!myProcess.HasExited) { try { ProcessModuleCollection modules = myProcess.Modules; int j = 0; for (j = 0; j <= modules.Count - 1; j++) { if ((modules.Item(j).FileName.ToLower.CompareTo(strFile.ToLower) == 0)) { myProcessArray.Add(myProcess); break; // TODO: might not be correct. Was : Exit For } } } catch (Exception exception) { } //MsgBox(("Error : " & exception.Message)) } } return myProcessArray; } } VB.Net: Imports System.Management Imports System.IO Module Module1 Friend myProcessArray As New ArrayList Private myProcess As Process Sub Main() Dim strFile As String = "c:\windows\system32\msi.dll" Dim a As ArrayList = getFileProcesses(strFile) For Each p As Process In a Debug.Print(p.ProcessName) Next End Sub Private Function getFileProcesses(ByVal strFile As String) As ArrayList myProcessArray.Clear() Dim processes As Process() = Process.GetProcesses Dim i As Integer For i = 0 To processes.GetUpperBound(0) - 1 myProcess = processes(i) If Not myProcess.HasExited Then Try Dim modules As ProcessModuleCollection = myProcess.Modules Dim j As Integer For j = 0 To modules.Count - 1 If (modules.Item(j).FileName.ToLower.CompareTo(strFile.ToLower) = 0) Then myProcessArray.Add(myProcess) Exit For End If Next j Catch exception As Exception 'MsgBox(("Error : " & exception.Message)) End Try End If Next i Return myProcessArray End Function End Module
The following was produced based on Iain Ballard's code dump. It is broken: it will occasionally lock up when you retrieve the handle name. This code doesn't contain any work-arounds for that issue, and .NET leaves few options: Thread.Abort can no longer abort a thread that's currently in a native method. So, with that disclaimer, here is the code to retrieve handles which has been adapted to work (apart from the occasional lock-up) both in 32 and 64 bit modes: using System; using System.Collections.Generic; using System.Linq; using System.Runtime.ConstrainedExecution; using System.Runtime.InteropServices; namespace BrokenHandleRetrieval { class Program { static void Main(string[] args) { Console.WriteLine("Enumerates open handles."); Console.WriteLine("This *will* lock up on calling HandleInfo.Name from time to time. Thread.Abort() won't help."); foreach (var hi in HandleUtil.GetHandles().Where(hi => hi.Type == HandleType.File)) Console.WriteLine("pid: " + hi.ProcessId + ", name: " + hi.Name); } } public enum HandleType { Unknown, Other, File, Directory, SymbolicLink, Key, Process, Thread, Job, Session, WindowStation, Timer, Desktop, Semaphore, Token, Mutant, Section, Event, KeyedEvent, IoCompletion, IoCompletionReserve, TpWorkerFactory, AlpcPort, WmiGuid, UserApcReserve, } public class HandleInfo { public int ProcessId { get; private set; } public ushort Handle { get; private set; } public int GrantedAccess { get; private set; } public byte RawType { get; private set; } public HandleInfo(int processId, ushort handle, int grantedAccess, byte rawType) { ProcessId = processId; Handle = handle; GrantedAccess = grantedAccess; RawType = rawType; } private static Dictionary<byte, string> _rawTypeMap = new Dictionary<byte, string>(); private string _name, _typeStr; private HandleType _type; public string Name { get { if (_name == null) initTypeAndName(); return _name; } } public string TypeString { get { if (_typeStr == null) initType(); return _typeStr; } } public HandleType Type { get { if (_typeStr == null) initType(); return _type; } } private void initType() { if (_rawTypeMap.ContainsKey(RawType)) { _typeStr = _rawTypeMap[RawType]; _type = HandleTypeFromString(_typeStr); } else initTypeAndName(); } bool _typeAndNameAttempted = false; private void initTypeAndName() { if (_typeAndNameAttempted) return; _typeAndNameAttempted = true; IntPtr sourceProcessHandle = IntPtr.Zero; IntPtr handleDuplicate = IntPtr.Zero; try { sourceProcessHandle = NativeMethods.OpenProcess(0x40 /* dup_handle */, true, ProcessId); // To read info about a handle owned by another process we must duplicate it into ours // For simplicity, current process handles will also get duplicated; remember that process handles cannot be compared for equality if (!NativeMethods.DuplicateHandle(sourceProcessHandle, (IntPtr) Handle, NativeMethods.GetCurrentProcess(), out handleDuplicate, 0, false, 2 /* same_access */)) return; // Query the object type if (_rawTypeMap.ContainsKey(RawType)) _typeStr = _rawTypeMap[RawType]; else { int length; NativeMethods.NtQueryObject(handleDuplicate, OBJECT_INFORMATION_CLASS.ObjectTypeInformation, IntPtr.Zero, 0, out length); IntPtr ptr = IntPtr.Zero; try { ptr = Marshal.AllocHGlobal(length); if (NativeMethods.NtQueryObject(handleDuplicate, OBJECT_INFORMATION_CLASS.ObjectTypeInformation, ptr, length, out length) != NT_STATUS.STATUS_SUCCESS) return; _typeStr = Marshal.PtrToStringUni((IntPtr) ((int) ptr + 0x58 + 2 * IntPtr.Size)); _rawTypeMap[RawType] = _typeStr; } finally { Marshal.FreeHGlobal(ptr); } } _type = HandleTypeFromString(_typeStr); // Query the object name if (_typeStr != null && GrantedAccess != 0x0012019f && GrantedAccess != 0x00120189 && GrantedAccess != 0x120089) // don't query some objects that could get stuck { int length; NativeMethods.NtQueryObject(handleDuplicate, OBJECT_INFORMATION_CLASS.ObjectNameInformation, IntPtr.Zero, 0, out length); IntPtr ptr = IntPtr.Zero; try { ptr = Marshal.AllocHGlobal(length); if (NativeMethods.NtQueryObject(handleDuplicate, OBJECT_INFORMATION_CLASS.ObjectNameInformation, ptr, length, out length) != NT_STATUS.STATUS_SUCCESS) return; _name = Marshal.PtrToStringUni((IntPtr) ((int) ptr + 2 * IntPtr.Size)); } finally { Marshal.FreeHGlobal(ptr); } } } finally { NativeMethods.CloseHandle(sourceProcessHandle); if (handleDuplicate != IntPtr.Zero) NativeMethods.CloseHandle(handleDuplicate); } } public static HandleType HandleTypeFromString(string typeStr) { switch (typeStr) { case null: return HandleType.Unknown; case "File": return HandleType.File; case "IoCompletion": return HandleType.IoCompletion; case "TpWorkerFactory": return HandleType.TpWorkerFactory; case "ALPC Port": return HandleType.AlpcPort; case "Event": return HandleType.Event; case "Section": return HandleType.Section; case "Directory": return HandleType.Directory; case "KeyedEvent": return HandleType.KeyedEvent; case "Process": return HandleType.Process; case "Key": return HandleType.Key; case "SymbolicLink": return HandleType.SymbolicLink; case "Thread": return HandleType.Thread; case "Mutant": return HandleType.Mutant; case "WindowStation": return HandleType.WindowStation; case "Timer": return HandleType.Timer; case "Semaphore": return HandleType.Semaphore; case "Desktop": return HandleType.Desktop; case "Token": return HandleType.Token; case "Job": return HandleType.Job; case "Session": return HandleType.Session; case "IoCompletionReserve": return HandleType.IoCompletionReserve; case "WmiGuid": return HandleType.WmiGuid; case "UserApcReserve": return HandleType.UserApcReserve; default: return HandleType.Other; } } } public static class HandleUtil { public static IEnumerable<HandleInfo> GetHandles() { // Attempt to retrieve the handle information int length = 0x10000; IntPtr ptr = IntPtr.Zero; try { while (true) { ptr = Marshal.AllocHGlobal(length); int wantedLength; var result = NativeMethods.NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS.SystemHandleInformation, ptr, length, out wantedLength); if (result == NT_STATUS.STATUS_INFO_LENGTH_MISMATCH) { length = Math.Max(length, wantedLength); Marshal.FreeHGlobal(ptr); ptr = IntPtr.Zero; } else if (result == NT_STATUS.STATUS_SUCCESS) break; else throw new Exception("Failed to retrieve system handle information."); } int handleCount = IntPtr.Size == 4 ? Marshal.ReadInt32(ptr) : (int) Marshal.ReadInt64(ptr); int offset = IntPtr.Size; int size = Marshal.SizeOf(typeof(SystemHandleEntry)); for (int i = 0; i < handleCount; i++) { var struc = (SystemHandleEntry) Marshal.PtrToStructure((IntPtr) ((int) ptr + offset), typeof(SystemHandleEntry)); yield return new HandleInfo(struc.OwnerProcessId, struc.Handle, struc.GrantedAccess, struc.ObjectTypeNumber); offset += size; } } finally { if (ptr != IntPtr.Zero) Marshal.FreeHGlobal(ptr); } } [StructLayout(LayoutKind.Sequential)] private struct SystemHandleEntry { public int OwnerProcessId; public byte ObjectTypeNumber; public byte Flags; public ushort Handle; public IntPtr Object; public int GrantedAccess; } } enum NT_STATUS { STATUS_SUCCESS = 0x00000000, STATUS_BUFFER_OVERFLOW = unchecked((int) 0x80000005L), STATUS_INFO_LENGTH_MISMATCH = unchecked((int) 0xC0000004L) } enum SYSTEM_INFORMATION_CLASS { SystemBasicInformation = 0, SystemPerformanceInformation = 2, SystemTimeOfDayInformation = 3, SystemProcessInformation = 5, SystemProcessorPerformanceInformation = 8, SystemHandleInformation = 16, SystemInterruptInformation = 23, SystemExceptionInformation = 33, SystemRegistryQuotaInformation = 37, SystemLookasideInformation = 45 } enum OBJECT_INFORMATION_CLASS { ObjectBasicInformation = 0, ObjectNameInformation = 1, ObjectTypeInformation = 2, ObjectAllTypesInformation = 3, ObjectHandleInformation = 4 } static class NativeMethods { [DllImport("ntdll.dll")] internal static extern NT_STATUS NtQuerySystemInformation( [In] SYSTEM_INFORMATION_CLASS SystemInformationClass, [In] IntPtr SystemInformation, [In] int SystemInformationLength, [Out] out int ReturnLength); [DllImport("ntdll.dll")] internal static extern NT_STATUS NtQueryObject( [In] IntPtr Handle, [In] OBJECT_INFORMATION_CLASS ObjectInformationClass, [In] IntPtr ObjectInformation, [In] int ObjectInformationLength, [Out] out int ReturnLength); [DllImport("kernel32.dll")] internal static extern IntPtr GetCurrentProcess(); [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr OpenProcess( [In] int dwDesiredAccess, [In, MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, [In] int dwProcessId); [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool CloseHandle( [In] IntPtr hObject); [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool DuplicateHandle( [In] IntPtr hSourceProcessHandle, [In] IntPtr hSourceHandle, [In] IntPtr hTargetProcessHandle, [Out] out IntPtr lpTargetHandle, [In] int dwDesiredAccess, [In, MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, [In] int dwOptions); } }
This is probably irrelevant and if it is please someone comment but there was a work-around I've used in explorer before to get around file locks. If a file was locked by a process that had died Windows often wouldn't let you delete it but if you created a new file of the same name somewhere else, moved it to the folder it would succeed. You could then delete the new file and all was well. To use this for your app you'd have to be able to read the file and hold it in memory before you did this then you write it back out after you'd got rid of the old one. Maybe it will help, maybe not but it's worth trying.
Try Unlocker. If you try and delete the file that is locked by another process, it will list the process(es) that have the file locked. You can then unlock the file by shutting down those processes.
foreach (var process in Process.GetProcessesByName("excel")) //whatever you need to close { if (process.MainWindowTitle.Contains("test.xlsx")) { process.Kill(); break; } } or foreach (var process in Process.GetProcesses()) { if (process.MainWindowTitle.Contains("test.dat")) { process.Kill(); break; } }
I believe that you need code running in kernel mode to completely answer the question (but I haven't looked at the restart manager API). You can enumerate all processes and their modules - so if the file you're looking for is a module (DLL, EXE, OCX...), you're good to go. But if it's a text file for example, you have to look at the kernel handle table which you cannot see from user mode. Handle.exe has a kernel driver in order to do that.
I rewrote the GetProcessesLockingFile() method in the solution. The code was not working. For example, you have a folder "C:\folder1\folder2" and a process in folder2 (process1). If the process was running, GetProcessesLockingFile() was returning "C:\folder1\folder2". So the condition if (files.Contains(filePath)) => if ("C:\folder1\folder2".contains("C:\folder1\folder2\process1")) was never true. So this is my solution: public static List<Process> GetProcessesLockingFile(FileInfo file) { var procs = new List<Process>(); var processListSnapshot = Process.GetProcesses(); foreach (var process in processListSnapshot) { if (process.Id <= 4) { continue; } // system processes List<string> paths = GetFilesLockedBy(process); foreach (string path in paths) { string pathDirectory = path; if (!pathDirectory.EndsWith(Constants.DOUBLE_BACKSLASH)) { pathDirectory = pathDirectory + Constants.DOUBLE_BACKSLASH; } string lastFolderName = Path.GetFileName(Path.GetDirectoryName(pathDirectory)); if (file.FullName.Contains(lastFolderName)) { procs.Add(process); } } } return procs; } Or with a string parameter: public static List<Process> GetProcessesLockingFile(string filePath) { var procs = new List<Process>(); var processListSnapshot = Process.GetProcesses(); foreach (var process in processListSnapshot) { if (process.Id <= 4) { continue; } // system processes List<string> paths = GetFilesLockedBy(process); foreach (string path in paths) { string pathDirectory = path; if (!pathDirectory.EndsWith(Constants.DOUBLE_BACKSLASH)) { pathDirectory = pathDirectory + Constants.DOUBLE_BACKSLASH; } string lastFolderName = Path.GetFileName(Path.GetDirectoryName(pathDirectory)); if (filePath.Contains(lastFolderName)) { procs.Add(process); } } } return procs; }
You absolutely don't need to run in Kernel mode (!!!) It's a Win32 FAQ since Windows 95 (!) (in C, Google groups, Win32) : read the handle table, from User mode of course, and get the PID from the File handle ...
Using dotnet core (net6) I solved this problem by using the win32 restart manager (as others have also mentioned). However some of the linked articles have elaborate code importing DLLs and calling those. After finding an app to kill processes that lock a file written by meziantou. I found out that he publishes .Net wrappers for win32 dlls (including the restart manager). Leveraging his work, I was able to fix this problem with the following code: using Meziantou.Framework.Win32; public static IEnumerable<Process> GetProcessesLockingFile(string filePath) { using var session = RestartManager.CreateSession(); session.RegisterFile(filePath); return session.GetProcessesLockingResources(); }
simpler with linq: public void KillProcessesAssociatedToFile(string file) { GetProcessesAssociatedToFile(file).ForEach(x => { x.Kill(); x.WaitForExit(10000); }); } public List<Process> GetProcessesAssociatedToFile(string file) { return Process.GetProcesses() .Where(x => !x.HasExited && x.Modules.Cast<ProcessModule>().ToList() .Exists(y => y.FileName.ToLowerInvariant() == file.ToLowerInvariant()) ).ToList(); }