繁体   English   中英

从Windows 10 1709开始,正常重启重启Explorer.exe,Fall Creators Update aka Redstone 3

[英]Gracefully restarting explorer.exe as of Windows 10 1709, Fall Creators Update aka Redstone 3

对于Windows 10 1703的当前版本(创建者更新),我有一个叫C#的小应用在安装过程中被要求重新启动explorer.exe。 这是为了帮助刷新任务栏/注册表项,以便安装后虚拟外设之一将出现在任务栏上,而无需重新启动。

using System;
using System.Diagnostics;

namespace RestartExplorer
{
    class Program
    {
        static int Main(string[] args)
        {
            var process = Process.GetProcessesByName("explorer")[0];
            process.Kill();
            Process.Start("explorer");
            return 0;
        }
    }
}

这在Redstone 2中工作正常,但在当前的Insiders Preview Windows 10 1709 Redstone 3内部版本16294.1.170916-2023 ,它不仅杀死了浏览器外壳,而且还杀死了所有打开的文件浏览器窗口。 这是一种超级侵入式操作,如果在发生这种情况时我在工作时打开了十几个窗口,我认为我对UX不会感到很高兴。

我验证了在CTRL + SHIFT 右键单击退出资源管理器”的任务栏上也显示了相同的差异行为,而不仅仅是我的小应用程序。

因此,如果我要确保用户的窗口不会丢失,那么现在应该如何重新启动资源管理器,或者更好的方法是,有没有一种更好的方法来获得我想要做的最终结果?

使用重新启动管理器API关闭所有打开的资源管理器。 它将重新启动所有已关闭的服务器。 唯一的缺点是重新启动的应用程序将被激活,因此您必须围绕应用程序失去焦点进行编码。

参见https://msdn.microsoft.com/zh-cn/library/windows/desktop/aa373649(v=vs.85).aspx

var sessionKey = Guid.NewGuid().ToString();
NativeMethods.RmStartSession(out IntPtr session, 0, sessionKey).CheckError();
try
{
    NativeMethods.RmRegisterResources(session, 1, new[] { Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "explorer.exe") }, 0, null, 0, null).CheckError();
    NativeMethods.RmShutdown(session, 0, null).CheckError();
    NativeMethods.RmRestart(session, 0, null).CheckError();
}
finally
{
    NativeMethods.RmEndSession(session);
}

您还将需要以下NativeMethods

public static class NativeMethods
{
    [StructLayout(LayoutKind.Sequential)]
    internal struct RM_UNIQUE_PROCESS
    {
        public int dwProcessId;
        public com.FILETIME ProcessStartTime;
    }

    [Flags]
    internal enum RM_SHUTDOWN_TYPE : uint
    {
        RmForceShutdown = 0x1,
        RmShutdownOnlyRegistered = 0x10
    }

    internal delegate void RM_WRITE_STATUS_CALLBACK(UInt32 nPercentComplete);

    [DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)]
    internal static extern int RmStartSession(out IntPtr pSessionHandle, int dwSessionFlags, string strSessionKey);

    [DllImport("rstrtmgr.dll")]
    internal static extern int RmEndSession(IntPtr pSessionHandle);

    [DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)]
    internal static extern int RmRegisterResources(IntPtr pSessionHandle, UInt32 nFiles, string[] rgsFilenames, UInt32 nApplications, RM_UNIQUE_PROCESS[] rgApplications, UInt32 nServices, string[] rgsServiceNames);

    [DllImport("rstrtmgr.dll")]
    internal static extern int RmShutdown(IntPtr pSessionHandle, RM_SHUTDOWN_TYPE lActionFlags, RM_WRITE_STATUS_CALLBACK fnStatus);

    [DllImport("rstrtmgr.dll")]
    internal static extern int RmRestart(IntPtr pSessionHandle, int dwRestartFlags, RM_WRITE_STATUS_CALLBACK fnStatus);

    [DllImport("kernel32.dll")]
    internal static extern bool GetProcessTimes(IntPtr hProcess, out com.FILETIME lpCreationTime, out com.FILETIME lpExitTime, out com.FILETIME lpKernelTime, out com.FILETIME lpUserTime);
}

基于@Tim代码并在此处提供了更有用的示例,这是我刚刚编写的用于与Restart Manager交互的类:

https://gist.github.com/falahati/34b23831733151460de1368c5fba8e93

这是一个例子:

using RestartManager;

public static class Test
{
    public static void Main()
    {
        using (var rm = new RestartManagerSession())
        {
            // add all processes having the name `explorer`
            rm.RegisterProcess(Process.GetProcessesByName("explorer"));

            // you can also add explorer.exe specifically by 
            // using the `RegisterProcessFile()` method
            //rm.RegisterProcessFile(new FileInfo(Path.Combine(
            //  Environment.GetFolderPath(Environment.SpecialFolder.Windows),
            //  "explorer.exe"
            //)));

            rm.Shutdown(RestartManagerSession.ShutdownType.Normal);
            rm.Restart();
        }
    }
}

如果您想更具体一些,还可以使用以下代码来获取Explorer的主要过程:

[DllImport("user32")]
private static extern IntPtr GetShellWindow();

[DllImport("user32", SetLastError = true)]
private static extern uint GetWindowThreadProcessId(IntPtr windowHandle, out uint processId);

public static Process GetShellProcess()
{
    try
    {
        var shellWindowHandle = GetShellWindow();

        if (shellWindowHandle != IntPtr.Zero)
        {
            GetWindowThreadProcessId(shellWindowHandle, out var shellPid);

            if (shellPid > 0)
            {
                return Process.GetProcessById((int) shellPid);
            }
        }
    }
    catch (Exception)
    {
        // ignored
    }

    return null;
}

然后,使用同一类通过Restart Manager重新启动它。 无论如何,它比Process.GetProcessesByName("explorer")[0]更好。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM