使用 C#,如何确定是哪个进程锁定了文件?

Posted

技术标签:

【中文标题】使用 C#,如何确定是哪个进程锁定了文件?【英文标题】:Using C#, how does one figure out what process locked a file? 【发布时间】:2010-10-26 00:42:33 【问题描述】:

在 Windows 中,我如何确定(使用 C#)哪个进程锁定了文件?

第三方工具很有帮助,但不是我想要的。

【问题讨论】:

文件夹锁定的任何解决方案?? 【参考方案1】:

这个问题的原始答案已经超过 7 年了。该代码保存在https://gist.github.com/i-e-b/2290426 如果您出于某种原因需要使用 Windows XP,这个旧版本可能适合您。

更好的答案是How to check for file lock?

我在下面复制了 Eric J 的答案(添加了 using 语句,以及与此处的旧代码匹配的类和方法名称)请注意,此答案的 cmets 可能已过时。

用户“Walkman”正在进行研究以改进旧代码,因为在某些情况下,重新启动管理器不会列出所有锁。参见 Github 仓库:https://github.com/Walkman100/FileLocks

像这样使用:

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

代码:

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;
        
    

【讨论】:

我已经多次运行此代码,其中很少有人尝试读取受保护的内存或错误读取访问文件的进程。很遗憾,但似乎这种和平的代码并没有准备好在生产附近的任何地方使用((((( @romkyns 你介意分享代码的最终版本吗?我对 SYSTEM_HANDLE_INFORMATION 进行了更改,但它无法在我的 XP x64 系统上以 32 或 64 模式运行。 @Mr.TA 好的,使用该代码发布an answer。但它用处不大,因为它偶尔会锁定。我最终使用了handle.exe。 Windows 目前根本不公开可用的 API 来获取此信息; handle.exe 是一个久经考验的 hack。 handle.exe 不是可分发软件的选项。见technet.microsoft.com/en-us/sysinternals/bb847944。 这不适用于 COM 端口(例如:“COM1:”),RmGetList 会产生异常“无法列出进程锁定资源。无法获取结果大小”【参考方案2】:

我在这里找到的代码, https://vmccontroller.svn.codeplex.com/svn/VmcController/VmcServices/DetectOpenFiles.cs

比 Iain 提供的代码更适合我。 Iain 的代码似乎获得了自己的锁。这是我对上面代码的略微修改版本,修改后返回锁定文件的字符串路径,而不是 FileSystemInfo 对象,

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
    

【讨论】:

这段代码似乎显示了给定进程打开的所有文件,但 OP 要求的恰恰相反。 +1 虽然这是相反的任务,但它可以用来完成有问题的任务 我测试过这段代码和Ians代码,我必须说我更喜欢这段代码的结果。不知道为什么有人会降级这个答案 任何带有完整源代码的完整示例使用 DetectOpenfile 类? @jpierson 嗯,你试过DetectOpenFiles.GetProcessesUsingFile(&lt;file-name&gt;)吗?【参考方案3】:

不是很简单,但在 Windows Vista 及更高版本上,您可以使用 Restart Manager APIs 查看谁在使用文件。 Internet Explorer caches settings 包含有关使用它来检测哪个进程打开了iexplore.exe 的详细信息。

省略了很多细节:

// 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);

【讨论】:

谢谢。这里还有一篇关于重启管理器主题的好文章:msdn.microsoft.com/en-us/magazine/cc163450.aspx。这里详细介绍了一种获取使用文件的进程的方法。 这确实应该是公认的答案。它是唯一不使用 3rd 方工具或未记录 API 的解决方案。 警告:这仅适用于桌面应用程序,不适用于 Windows 服务。 @gigi:服务的错误模式是什么?返回什么错误代码?看到 Restart Manager API 主要用于安装程序,并且从服务运行安装程序没有问题,我会惊讶地发现,Restart Manager 无法从服务运行。 @IInspectable:如果您转到上面链接的 Restart Manager API 的 MSDN 页面,您可以阅读:“Restart Manager 旨在用于桌面样式应用程序的开发。”你的例子也不例外。我的意思是你不能使用来自 c# windows 服务应用程序的 API。那是行不通的。但当然,您可以从该服务启动一个单独的桌面可执行文件。这可能行得通。【参考方案4】:

Handle,来自Windows Sysinternals。这是 Microsoft 提供的免费命令行实用程序。

你可以运行它,然后解析结果。

【讨论】:

不确定是否运行 3rd 方工具和解析输出算作“以编程方式”。 @Daniel 您需要安装它,拥有管理权限并依赖用户界面输出。想不出更优雅的解决方案,更不用说“程序化”解决方案了。 有时需要集成第三方工具。它节省了尝试重写其他人已经编写的代码的努力。您可以使用自己的部署包轻松打包 exe。 我不会认为它是第三款应用,因为它是由微软自己提供的。无论如何,你有更好的方法吗? 可以使用任何您认为简单的有效解决方案。我更喜欢坚持使用 .NET Framework 默认值。 3rd 方应用程序通常是我的最后手段【参考方案5】:

以下内容是根据 Iain Ballard 的代码转储生成的。它损坏:当您检索句柄名称时,它会偶尔锁定。此代码不包含针对该问题的任何解决方法,并且 .NET 留下了几个选项:Thread.Abort 不能再中止当前处于本机方法中的线程。

因此,根据免责声明,这里是检索句柄的代码,该句柄已适应在 32 位和 64 位模式下工作(除了偶尔的锁定):

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);
    

【讨论】:

【参考方案6】:

试试Unlocker。如果您尝试删除被另一个进程锁定的文件,它将列出锁定该文件的进程。然后,您可以通过关闭这些进程来解锁文件。

【讨论】:

oops 没有注意到您希望以编程方式使用它 这是一个很棒的应用程序,因此我投票赞成你的答案。 Sysinternals 的进程资源管理器也可以找到锁定文件的进程。只需在主菜单中使用 FindFind Handle 或 Dll。在文本框中输入文件的路径,然后单击搜索【参考方案7】:

我相信您需要在内核模式下运行代码才能完全回答这个问题(但我没有查看重启管理器 API)。

您可以枚举所有进程及其模块 - 因此,如果您要查找的文件是一个模块(DLL、EXE、OCX...),那么您就可以开始了。但是,例如,如果它是一个文本文件,则必须查看从用户模式看不到的内核句柄表。 Handle.exe 有一个内核驱动程序可以做到这一点。

【讨论】:

【参考方案8】:

我在解决方案中重写了GetProcessesLockingFile() 方法。代码不工作。

例如,您有一个文件夹 "C:\folder1\folder2" 和文件夹 2 (process1) 中的一个进程。如果进程正在运行,GetProcessesLockingFile() 将返回"C:\folder1\folder2"。所以条件if (files.Contains(filePath)) => if ("C:\folder1\folder2".contains("C:\folder1\folder2\process1")) 从来都不是真的。

所以这是我的解决方案:

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;

或者带字符串参数:

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;

【讨论】:

GetFilesLockedBy() 方法和Constants 类/枚举在哪里?【参考方案9】:

您绝对不需要在内核模式下运行 (!!!) 这是自 Windows 95 以来的 Win32 常见问题(!)(在 C、Google 组、Win32 中):当然从用户模式读取句柄表,并从文件句柄中获取 PID ...

【讨论】:

有比你的简短总结更有用的细节吗?

以上是关于使用 C#,如何确定是哪个进程锁定了文件?的主要内容,如果未能解决你的问题,请参考以下文章

如何找出使用 .NET 锁定文件的进程?

手动将文件与流程关联

删除被进程访问而锁定的文件

如果给定文件或目录被锁定(由任何进程使用),如何检查命令行?

如何确定在 C# ASP.NET CORE MVC 5.0 中选择了哪个单选按钮

允许确定哪个 PID 正在使用它们的跨平台同步原语