... var rm = new RestartManager(); rm.RestartExplorerProcesses(); ... /// /// A utility class to restart programs the most gracefully possible. Wraps Windows Restart Manager API. This class cannot be inherited. /// public sealed class RestartManager { /// /// The default kill timeout value (2000). /// public const int DefaultKillTimeout = 2000; /// /// The default retry count value (10). /// public const int DefaultRetryCount = 10; /// /// The default retry timeout value (100). /// public const int DefaultRetryTimeout = 100; /// /// Initializes a new instance of the class. /// public RestartManager() { KillTimeout = DefaultKillTimeout; RetryCount = DefaultRetryCount; RetryTimeout = DefaultRetryTimeout; } /// /// Gets or sets the kill timeout in ms. /// /// The kill timeout. public int KillTimeout { get; set; } /// /// Gets or sets the retry count. /// /// The retry count. public int RetryCount { get; set; } /// /// Gets or sets the retry timeout in ms. /// /// The retry timeout. public int RetryTimeout { get; set; } /// /// Restarts the Windows Explorer processes. /// /// The stopped action. public void RestartExplorerProcesses() => RestartExplorerProcesses(null, false, out var error); /// /// Restarts the Windows Explorer processes. /// /// The stopped action. public void RestartExplorerProcesses(ContextCallback stoppedAction) => RestartExplorerProcesses(stoppedAction, false, out var error); /// /// Restarts the Windows Explorer processes. /// /// The stopped action. /// if set to true errors may be throw in case of Windows Restart Manager errors. public void RestartExplorerProcesses(ContextCallback stoppedAction, bool throwOnError) => RestartExplorerProcesses(stoppedAction, throwOnError, out var error); /// /// Restarts the Windows Explorer processes. /// /// The stopped action. /// if set to true errors may be throw in case of Windows Restart Manager errors. /// The error, if any. public void RestartExplorerProcesses(ContextCallback stoppedAction, bool throwOnError, out Exception error) { var explorers = Process.GetProcessesByName("explorer").Where(p => IsExplorer(p)).ToArray(); Restart(explorers, stoppedAction, throwOnError, out error); } /// /// Restarts the processes locking a specific file. /// /// The file path. /// The stopped action. /// if set to true errors may be throw in case of Windows Restart Manager errors. /// The error, if any. /// path is null. public void RestartProcessesLockingFile(string path, ContextCallback stoppedAction, bool throwOnError, out Exception error) { if (path == null) throw new ArgumentNullException(nameof(path)); var lockers = GetLockingProcesses(path, false, throwOnError, out error); if (error != null) return; Restart(lockers, stoppedAction, throwOnError, out error); } /// /// Restarts the Windows Explorer processes locking a specific file. /// /// The file path. /// The stopped action. /// if set to true errors may be throw in case of Windows Restart Manager errors. /// The error, if any. /// path is null. public void RestartExplorerProcessesLockingFile(string path, ContextCallback stoppedAction, bool throwOnError, out Exception error) { if (path == null) throw new ArgumentNullException(nameof(path)); var processes = GetLockingProcesses(path, false, throwOnError, out error); if (error != null) return; var explorers = processes.Where(p => IsExplorer(p)).ToArray(); Restart(explorers, stoppedAction, throwOnError, out error); } /// /// Determines whether the specified process is Windows Explorer. /// /// The process. /// true if the specified process is Windows Explorer; otherwise, false. public static bool IsExplorer(Process process) { if (process == null) return false; string explorerPath = Path.Combine(Environment.GetEnvironmentVariable("windir"), "explorer.exe"); return string.Compare(process.MainModule.FileName, explorerPath, StringComparison.OrdinalIgnoreCase) == 0; } /// /// Gets a list of processes locking a specific file. /// /// The file path. /// if set to true list only restartable processes. /// if set to true errors may be throw in case of Windows Restart Manager errors. /// The error, if any. /// A list of processes. /// filePath is null. public IReadOnlyList GetLockingProcesses(string filePath, bool onlyRestartable, bool throwOnError, out Exception error) { if (filePath == null) throw new ArgumentNullException(nameof(filePath)); return GetLockingProcesses(new[] { filePath }, onlyRestartable, throwOnError, out error); } // NOTE: file name comparison seems to be case insensitive /// /// Gets a list of processes locking a list of specific files. /// /// The files paths. /// if set to true list only restartable processes. /// if set to true errors may be throw in case of Windows Restart Manager errors. /// The error, if any. /// A list of processes. /// filePaths is null. public IReadOnlyList GetLockingProcesses(IEnumerable filePaths, bool onlyRestartable, bool throwOnError, out Exception error) { if (filePaths == null) throw new ArgumentNullException(nameof(filePaths)); var processes = new List(); var paths = new List(filePaths); var s = new StringBuilder(256); int hr = RmStartSession(out int session, 0, s); if (hr != 0) { error = new Win32Exception(hr); if (throwOnError) throw error; return processes; } try { hr = RmRegisterResources(session, paths.Count, paths.ToArray(), 0, null, 0, null); if (hr != 0) { error = new Win32Exception(hr); if (throwOnError) throw error; return processes; } int procInfo = 0; int rebootReasons = RmRebootReasonNone; hr = RmGetList(session, out int procInfoNeeded, ref procInfo, null, ref rebootReasons); if (hr == 0) { error = null; return processes; } if (hr != ERROR_MORE_DATA) { error = new Win32Exception(hr); if (throwOnError) throw error; return processes; } var processInfo = new RM_PROCESS_INFO[procInfoNeeded]; procInfo = processInfo.Length; hr = RmGetList(session, out procInfoNeeded, ref procInfo, processInfo, ref rebootReasons); if (hr != 0) { error = new Win32Exception(hr); if (throwOnError) throw error; return processes; } for (int i = 0; i < procInfo; i++) { try { if (processInfo[i].bRestartable || !onlyRestartable) { var process = Process.GetProcessById(processInfo[i].Process.dwProcessId); if (process != null) { processes.Add(process); } } } catch (Exception e) { error = e; // do nothing, fail silently return processes; } } error = null; return processes; } finally { RmEndSession(session); } } /// /// Restarts the specified processes. /// /// The processes. /// The stopped action. /// if set to true errors may be throw in case of Windows Restart Manager errors. /// The error, if any. /// processes is null. public void Restart(IEnumerable processes, ContextCallback stoppedAction, bool throwOnError, out Exception error) { if (processes == null) throw new ArgumentNullException(nameof(processes)); if (processes.Count() == 0) { error = null; return; } var s = new StringBuilder(256); int hr = RmStartSession(out int session, 0, s); if (hr != 0) { error = new Win32Exception(hr); if (throwOnError) throw error; return; } try { var list = new List(); foreach (var process in processes) { var p = new RM_UNIQUE_PROCESS() { dwProcessId = process.Id }; long l = process.StartTime.ToFileTime(); p.ProcessStartTime.dwHighDateTime = (int)(l >> 32); p.ProcessStartTime.dwLowDateTime = (int)(l & 0xFFFFFFFF); list.Add(p); } hr = RmRegisterResources(session, 0, null, list.Count, list.ToArray(), 0, null); if (hr != 0) { error = new Win32Exception(hr); if (throwOnError) throw error; return; } int procInfo = 0; int rebootReasons = RmRebootReasonNone; hr = RmGetList(session, out int procInfoNeeded, ref procInfo, null, ref rebootReasons); if (hr == 0) { error = null; return; } if (hr != ERROR_MORE_DATA) { error = new Win32Exception(hr); if (throwOnError) throw error; return; } var processInfo = new RM_PROCESS_INFO[procInfoNeeded]; procInfo = processInfo.Length; hr = RmGetList(session, out procInfoNeeded, ref procInfo, processInfo, ref rebootReasons); if (hr != 0) { error = new Win32Exception(hr); if (throwOnError) throw error; return; } if (procInfo == 0) { error = null; return; } bool hasError = false; int wtk = GetWaitToKillTimeout(); var sw = new Stopwatch(); sw.Start(); bool finished = false; var timer = new Timer((state) => { if (!finished) { HardKill(processes); } }, null, wtk + 2000, Timeout.Infinite); hr = RmShutdown(session, RmForceShutdown, percent => { // add progress info code if needed }); sw.Stop(); if (hr != 0) { if (!IsNonFatalError(hr)) { error = new Win32Exception(hr); if (throwOnError) throw error; return; } hasError = true; } if (hasError) { HardKill(processes); } if (stoppedAction != null) { int retry = RetryCount; while (retry > 0) { try { stoppedAction(session); break; } catch { retry--; Thread.Sleep(RetryTimeout); } } } hr = RmRestart(session, 0, percent2 => { // add progress info code if needed }); if (hr != 0) { error = new Win32Exception(hr); if (throwOnError) throw error; return; } } finally { RmEndSession(session); } error = null; } private void HardKill(IEnumerable processes) { // need a hard restart foreach (var process in processes) { try { process.Refresh(); if (!process.HasExited) { process.Kill(); } } catch { // do nothing } } Thread.Sleep(KillTimeout); } private static bool IsNonFatalError(int hr) => hr == ERROR_FAIL_NOACTION_REBOOT || hr == ERROR_FAIL_SHUTDOWN || hr == ERROR_SEM_TIMEOUT || hr == ERROR_CANCELLED; /// /// Gets the root Windows Explorer process. /// /// An instance of the Process type or null. public static Process GetRootExplorerProcess() { Process oldest = null; foreach (var process in EnumExplorerProcesses()) { if (oldest == null || process.StartTime < oldest.StartTime) { oldest = process; } } return oldest; } /// /// Enumerates Windows Explorer processes. /// /// A list of Windows Explorer processes. public static IEnumerable EnumExplorerProcesses() { string explorerPath = Path.Combine(Environment.GetEnvironmentVariable("windir"), "explorer.exe"); foreach (var process in Process.GetProcessesByName("explorer")) { if (string.Compare(process.MainModule.FileName, explorerPath, StringComparison.OrdinalIgnoreCase) == 0) yield return process; } } /// /// Enumerates a specific process' windows. /// /// The process. /// A list of windows handles. /// process is null. public static IReadOnlyList EnumProcessWindows(Process process) { if (process == null) throw new ArgumentNullException(nameof(process)); var handles = new List(); EnumWindows((h, p) => { GetWindowThreadProcessId(h, out int processId); if (processId == process.Id) { handles.Add(h); } return true; }, IntPtr.Zero); return handles; } // https://technet.microsoft.com/en-us/library/cc976045.aspx /// /// Gets the wait to kill timeout, that is, how long the system waits for services to stop after notifying the service that the system is shutting down /// /// A number of milliseconds. public static int GetWaitToKillTimeout() { using (var key = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control", false)) { if (key != null) { var v = key.GetValue("WaitToKillServiceTimeout", 0); if (v != null && int.TryParse(v.ToString(), out int i)) return i; } return 0; } } [DllImport("user32.dll")] private static extern int GetWindowThreadProcessId(IntPtr handle, out int processId); private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); [DllImport("user32.dll")] private static extern bool EnumWindows(EnumWindowsProc callback, IntPtr extraData); [DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)] private static extern int RmStartSession(out int pSessionHandle, int dwSessionFlags, StringBuilder strSessionKey); [DllImport("rstrtmgr.dll")] private static extern int RmEndSession(int pSessionHandle); [DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)] private static extern int RmRegisterResources(int pSessionHandle, int nFiles, string[] rgsFilenames, int nApplications, RM_UNIQUE_PROCESS[] rgApplications, int nServices, string[] rgsServiceNames); [DllImport("rstrtmgr.dll")] private static extern int RmGetList(int dwSessionHandle, out int pnProcInfoNeeded, ref int pnProcInfo, [In, Out] RM_PROCESS_INFO[] rgAffectedApps, ref int lpdwRebootReasons); [DllImport("rstrtmgr.dll")] private static extern int RmShutdown(int dwSessionHandle, int lActionFlags, StatusHandler fnStatus); [DllImport("rstrtmgr.dll")] private static extern int RmRestart(int dwSessionHandle, int dwRestartFlags, StatusHandler fnStatus); /// /// Represents the method that handles status updates. /// /// The percentage completed. public delegate void StatusHandler(int percentComplete); private const int RmRebootReasonNone = 0; private const int RmForceShutdown = 1; private const int RmShutdownOnlyRegistered = 0x10; private const int ERROR_MORE_DATA = 234; private const int ERROR_FAIL_NOACTION_REBOOT = 350; private const int ERROR_FAIL_SHUTDOWN = 351; private const int ERROR_SEM_TIMEOUT = 121; private const int ERROR_CANCELLED = 1223; private const int CCH_RM_MAX_APP_NAME = 255; private const int CCH_RM_MAX_SVC_NAME = 63; [StructLayout(LayoutKind.Sequential)] private struct RM_UNIQUE_PROCESS { public int 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 = 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 RM_APP_STATUS AppStatus; public int TSSessionId; [MarshalAs(UnmanagedType.Bool)] public bool bRestartable; } [Flags] private enum RM_APP_STATUS { RmStatusUnknown = 0x0, RmStatusRunning = 0x1, RmStatusStopped = 0x2, RmStatusStoppedOther = 0x4, RmStatusRestarted = 0x8, RmStatusErrorOnStop = 0x10, RmStatusErrorOnRestart = 0x20, RmStatusShutdownMasked = 0x40, RmStatusRestartMasked = 0x80 } private enum RM_APP_TYPE { RmUnknownApp = 0, RmMainWindow = 1, RmOtherWindow = 2, RmService = 3, RmExplorer = 4, RmConsole = 5, RmCritical = 1000 } }