C#中如何调用Win API的RestartManager?是否有.NET封装?
在C#中使用RestartManager检查文件占用状态
1. .NET 内置封装情况
目前.NET框架(包括.NET Core/.NET 5+)并没有直接封装RestartManager的完整API,需要通过P/Invoke调用原生Windows API来实现功能。
2. C#中高效使用RestartManager的实现方式
你可以通过P/Invoke封装RestartManager的核心函数,完成目录下文件占用状态的检查,核心步骤包括启动会话、注册目标目录、获取占用进程列表、结束会话。以下是核心实现代码:
using System; using System.Runtime.InteropServices; public static class RestartManagerHelper { [DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)] private static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey); [DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)] private static extern int RmRegisterResources(uint sessionHandle, uint nFiles, string[] rgsFilenames, uint nApplications, [In] RM_UNIQUE_PROCESS[] rgApplications, uint nServices, string[] rgsServiceNames); [DllImport("rstrtmgr.dll")] private static extern int RmGetList(uint sessionHandle, out uint pnProcInfoNeeded, ref uint pnProcInfo, [Out] RM_PROCESS_INFO[] rgAffectedApps, ref uint lpdwRebootReasons); [DllImport("rstrtmgr.dll")] private static extern int RmEndSession(uint sessionHandle); [StructLayout(LayoutKind.Sequential)] private struct RM_UNIQUE_PROCESS { public uint dwProcessId; public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct RM_PROCESS_INFO { public RM_UNIQUE_PROCESS Process; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string strAppName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 512)] public string strServiceShortName; public RM_APP_TYPE ApplicationType; public uint AppStatus; public uint TSSessionId; [MarshalAs(UnmanagedType.Bool)] public bool bRestartable; } private enum RM_APP_TYPE { RmUnknownApp = 0, RmMainWindow = 1, RmOtherWindow = 2, RmService = 3, RmExplorer = 4, RmConsole = 5, RmCritical = 1000 } public static bool IsDirectoryInUse(string directoryPath) { uint sessionHandle = 0; string sessionKey = Guid.NewGuid().ToString(); int result = RmStartSession(out sessionHandle, 0, sessionKey); if (result != 0) throw new System.ComponentModel.Win32Exception(result); try { string[] resources = new string[] { directoryPath }; result = RmRegisterResources(sessionHandle, (uint)resources.Length, resources, 0, null, 0, null); if (result != 0) throw new System.ComponentModel.Win32Exception(result); uint procInfoNeeded = 0; uint procInfo = 0; uint rebootReasons = 0; result = RmGetList(sessionHandle, out procInfoNeeded, ref procInfo, null, ref rebootReasons); if (result != 0 && result != 234) // 234表示需要分配更大缓冲区 throw new System.ComponentModel.Win32Exception(result); if (procInfoNeeded == 0) return false; RM_PROCESS_INFO[] processInfo = new RM_PROCESS_INFO[procInfoNeeded]; procInfo = procInfoNeeded; result = RmGetList(sessionHandle, out procInfoNeeded, ref procInfo, processInfo, ref rebootReasons); if (result != 0) throw new System.ComponentModel.Win32Exception(result); return procInfo > 0; } finally { RmEndSession(sessionHandle); } } }
3. 删除前检查文件可删除性的补充说明
- 仅通过
File.GetAccessControl这类权限检查无法完全判断文件是否可删除,因为文件可能被进程占用、处于锁定状态,或存在其他系统级限制。 - RestartManager是Windows原生的可靠方案,能准确识别占用文件的进程,效率优于遍历文件逐一检查的方式。
- 另一种替代方案是尝试获取文件的排他锁:若能成功获取则文件未被占用,若抛出
IOException则表示文件被占用,但这种方式无法获取具体占用进程,且需要遍历目录下所有文件,效率较低。
内容的提问来源于stack exchange,提问作者Tripp Sautner
相关产品推荐
相关产品推荐

