如何检查文件锁定?
如何检查文件锁定?
这个问题已经有答案了:
是否有任何方法可以在不使用try/catch块的情况下检查文件是否已锁定?
目前,我知道的唯一方法是只需打开文件并捕获任何System.IO.IOException
。
admin 更改状态以发布 2023年5月25日
其他答案依赖于旧信息。这个提供了更好的解决方案。
很久以前,在Windows中可靠地获取锁定文件的进程列表是不可能的,因为Windows根本没有跟踪这些信息。为了支持Restart Manager API,现在跟踪这些信息。 Restart Manager API从Windows Vista和Windows Server 2008开始提供支持(Restart Manager:运行时要求)。
我编写了代码,输入一个文件路径,返回锁定该文件的所有进程的List
。
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); ////// Find out what process(es) have a lock on the specified file. /// /// Path of the file. ///Processes locking the file ///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) /// /// static public ListWhoIsLocking(string path) { uint handle; string key = Guid.NewGuid().ToString(); List processes = new List (); 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 ((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; } }
更新
这里还有另一个讨论和示例代码,介绍如何使用Restart Manager API。
当我面临类似的问题时,我使用了以下代码:
public class FileManager { private string _fileName; private int _numberOfTries; private int _timeIntervalBetweenTries; private FileStream GetStream(FileAccess fileAccess) { var tries = 0; while (true) { try { return File.Open(_fileName, FileMode.Open, fileAccess, Fileshare.None); } catch (IOException e) { if (!IsFileLocked(e)) throw; if (++tries > _numberOfTries) throw new MyCustomException("The file is locked too long: " + e.Message, e); Thread.Sleep(_timeIntervalBetweenTries); } } } private static bool IsFileLocked(IOException exception) { int errorCode = Marshal.GetHRForException(exception) & ((1 << 16) - 1); return errorCode == 32 || errorCode == 33; } // other code }