如果Windows资源管理器在特定路径上打开,请不要创建新实例



我使用以下代码,这样当用户单击按钮时,Windows资源管理器的实例就会在特定路径上打开。但这会导致Explorer的一个新实例被打开。

我想更改它,这样,如果Explorer已经在同一路径中打开,程序就不会创建新的进程,而是将打开的实例放在前面。

private void button_Click(object sender, EventArgs e)
{
    if (Directory.Exists(myPath))
        Process filesFolder =  Process.Start("explorer.exe", Conf.FilesLocation);               
}

您可以使用"open"谓词,它将在资源管理器中打开目录,如果您向现有的explorer.exe传递一个已经打开的目录,则会重复使用它:因此,假设Conf.FilesLocation是一个目录:

        var proc = new ProcessStartInfo();
        proc.FileName = Conf.FilesLocation;
        proc.Verb = "open";
        proc.WindowStyle = ProcessWindowStyle.Hidden;
        Process.Start(proc );

我发现也可以使用file://协议,当你这样做时,如果文件夹已经打开,Windows似乎会将焦点放在文件夹上,而不是打开另一个窗口

Process.Start("file://" + Conf.FilesLocation);

尽管@nos的答案大部分都很好(有时,它会以一种不确定的方式创建另一个资源管理器窗口,尽管它可能已经存在),但我对Process.Start(proc)打开现有窗口所花费的时间感到不满意,有时是2到4秒。

因此,通过调整一些VB.NET代码,我实现了一种非常快速的方式来重用指向所需文件夹的现有资源管理器窗口:

首先,添加COM引用:

using Shell32;//Shell32.dll for ShellFolderView

using SHDocVw;//Microsoft Internet Controls for IShellWindows

    [DllImport("user32.dll")]
    public static extern int ShowWindow(IntPtr Hwnd, int iCmdShow);
    [DllImport("user32.dll")]
    public static extern bool IsIconic(IntPtr Hwnd);
    public static bool ShowInExplorer(string folderName) 
    {
        var SW_RESTORE = 9;
        var exShell = (IShellDispatch2)Activator.CreateInstance(                                           
                            Type.GetTypeFromProgID("Shell.Application"));
        
        foreach (ShellBrowserWindow w in (IShellWindows) exShell.Windows())
        {
              
            if (w.Document is ShellFolderView)
                {
                    var expPath = w.Document.FocusedItem.Path;
                    if (!Directory.Exists(Path.GetDirectoryName(expPath)) ||
                        Path.GetDirectoryName(expPath) != folderName) continue;
                    if (IsIconic(new IntPtr(w.HWND)))
                    {
                        w.Visible = false;
                        w.Visible = true;
                        ShowWindow(new IntPtr(w.HWND),SW_RESTORE);
                        break;
                    }
                    else
                    {
                        w.Visible = false;
                        w.Visible = true;
                        break;
                    }
                }
         }
    }

尽管我们对ShellFolderView对象感兴趣,并且foreach (ShellBrowserWindow w in (ShellFolderView) exShell.Windows())更符合逻辑,但不幸的是,ShellFolderView没有实现IEnumerable,因此,没有foreach:(

无论如何,这是一种非常快速(200毫秒)的方式来选择并闪烁正确的已打开的资源管理器窗口。

对我来说,这里的解决方案都不能在Win10中工作。由于Marcelo的解决方案exShell.Windows()总是空的,Josh的解决方案不适用于目录,您将得到file does not exist错误,而nos的解决方案抛出access denied错误。

这是我的工作解决方案:

    [DllImport("user32.dll")]
    private static extern bool SetForegroundWindow(IntPtr Hwnd);
    [DllImport("user32.dll")]
    private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
    public static void ShowInExplorer(string filePath) {
        IntPtr hWnd = FindWindow("CabinetWClass", Path.GetDirectoryName(filePath));
        if(hWnd != IntPtr.Zero) {
            SetForegroundWindow(hWnd);
        } else {
            Process.Start("explorer.exe", Path.GetDirectoryName(filePath));
        }
    }

Windows资源管理器具有窗口类CabinetWClass,并将标题设置为浏览的目录。

因此,上面的代码检查是否存在具有特定目录的资源管理器窗口。

如果它确实存在,则将窗口放在前面,否则将使用指定的目录启动资源管理器的新实例。请注意,您需要以给定的路径作为参数专门启动explorer.exe,否则它将引发access denied错误。

最新更新