J. Hajjar
J. Hajjar

Reputation: 127

Close open Explorer windows without terminating explorer.exe

I've tried searching but nothing really matches my demand.

I don't want explorer.exe to be terminated or restarted. I just want any open explorer windows to close.

Upvotes: 1

Views: 3277

Answers (4)

Zimba
Zimba

Reputation: 3691

By default, explorer runs as single process, and any windows that open are just a thread of the process.
Normally, to close a program, you'd send a close message to the process. In this case, closing explorer.exe will close all explorer windows.

To close individual windows, you'd open each window via it's own process. This can be done via registry setting or enabling under View->Options->View->Advanced Settings: "Launch ... separate process"

a) Find PID (process ID) of window you wanna close.

via taskmanager:
1. In list of processes, click the arrow to the left of "Windows Explorer"
2. Check the window name matches the window you wanna close
3. Right click on "Windows Explorer", click "Go to Details"
4. Record the pid

via CMD:
tasklist /V /FI "IMAGENAME eq explorer.exe"

If each explorer window is open in it's own process, the above command would display the window title in the last column.
Otherwise "N/A" would be displayed.

The pid of all explorer windows would be the same. Explorer.exe processes have their own pid, and title "N/A"
If 'separate process' has been enabled eg. via Folder View option, then each window can be closed via the process id & filter option of taskkill.

To close, the desired window has to be activated first, otherwise closing with pid will close the last active window, or closing with window title filter will give error:

INFO: No tasks running with the specified criteria.

b) taskkill /pid <pid> will close the last active window.
Repeating this command will the next window.

or taskkill /im explorer.exe /fi "windowtitle eq <window name>"
or taskkill /fi "IMAGENAME eq explorer.exe" /fi "windowtitle eq <window name>"

< window name > is not case sensitive
If full path in title bar has been enabled in Folder view, then include full path or wildcards.

To close all explorer windows:
taskkill /im explorer.exe

Notes:

  1. To activate explorer window, issue same command to open the window, if window reusing is enabled.
  2. The pid of explorer window(ing) process is in the last row of the response table, in column "PID"; can be accessed via FOR loop.
  3. A vbs workaround to close window from @HelpingHand:
    https://superuser.com/questions/1263315/how-to-close-a-particular-opened-folder-using-cmd-or-batch-file
  4. A vbs workaround to activate window:
    http://superuser.com/questions/327676/application-to-automatically-switch-between-two-applications-in-windows

Tested on Win 10

Upvotes: 0

TnTinMn
TnTinMn

Reputation: 11801

The following alternative uses the COM API of the Shell object to retrieve and identify File Explorer windows. It requires the addition of the COM references to:

  • Microsoft Shell Controls And Automation
  • Microsoft Internet Controls

The object returned by Shell.Windows method is an IEnumerable. Each object in the collection is a SHDocVw.InternetExplorer instance. If the Document object is a Shell32.ShellFolderView, then the explorer is a File Explorer.

    private static void CloseExplorerWindows()

        {
        Shell32.Shell shell = new Shell32.Shell();

        // ref: Shell.Windows method
        // https://msdn.microsoft.com/en-us/library/windows/desktop/bb774107(v=vs.85).aspx
        System.Collections.IEnumerable windows = shell.Windows() as System.Collections.IEnumerable;
        if (windows != null)
            {
            // ref: ShellWindows object
            // https://msdn.microsoft.com/en-us/library/windows/desktop/bb773974(v=vs.85).aspx
            foreach (SHDocVw.InternetExplorer window in windows)
                {
                object doc = window.Document;
                if (doc != null && doc is Shell32.ShellFolderView)
                    {
                    window.Quit();  // closes the window
                    }
                }
            }
        }

Upvotes: 4

DarkSquirrel42
DarkSquirrel42

Reputation: 10267

    [DllImport("user32.dll")]
    private static extern bool EnumWindows(EnumWindowsDelegate lpEnumFunc, IntPtr lParam);
    [DllImport("user32.dll")]
    private static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, out IntPtr lpdwProcessId);
    [DllImport("user32.dll")]
    private static extern uint RealGetWindowClass(IntPtr hwnd, StringBuilder pszType, uint cchType);
    [DllImport("user32.dll")]
    static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);

    static uint WM_CLOSE = 0x10;

    private delegate bool EnumWindowsDelegate(IntPtr hwnd, IntPtr lParam);

    private static bool EnumWindowsCallback(IntPtr hwnd, IntPtr lParam)
    {
        IntPtr pid = new IntPtr();
        GetWindowThreadProcessId(hwnd, out pid);
        var wndProcess = System.Diagnostics.Process.GetProcessById(pid.ToInt32());
        var wndClass = new StringBuilder(255);
        RealGetWindowClass(hwnd, wndClass, 255);
        if (wndProcess.ProcessName == "explorer" && wndClass.ToString() == "CabinetWClass")
        {
            //hello file explorer window...

            SendMessage(hwnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero); // ... bye file explorer window
        }
        return (true);
    }

    static void Main()
    {
        EnumWindowsDelegate childProc = new EnumWindowsDelegate(EnumWindowsCallback);

        EnumWindows(childProc, IntPtr.Zero);

        Console.ReadKey();
    }

edit:
so i guess the only interesting thing is the callback which will be called by windows for each enumerated window (handle of said window in hwnd)

GetWindowThreadProcessId provides us with the processid for a given window handle

GetProcessById then provides us with a process object to read things like the process name from

RealGetWindowClass provides us with the registered class name for a given window handle

finally we can look to see if the process for the current window is the explorer and if the window class is "CabinetWClass", which is the window class for the normal file explorer window

last but not least, if our check is ok, send a WM_CLOSE message to kindly ask the window to close itself...

Upvotes: 4

J. Hajjar
J. Hajjar

Reputation: 127

    public static void CloseExplorerWindows() => EnumWindows(new EnumWindowsProc(EnumTheWindows), IntPtr.Zero);

    private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    private static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount);
    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    private static extern int GetWindowTextLength(IntPtr hWnd);
    [DllImport("user32.dll")]
    private static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);
    [DllImport("user32.dll")]
    private static extern bool IsWindowVisible(IntPtr hWnd);
    [DllImport("user32.dll", SetLastError = true)]
    static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);

    static uint WM_CLOSE = 0x10;

    private static bool EnumTheWindows(IntPtr hWnd, IntPtr lParam)
    {
        int size = GetWindowTextLength(hWnd);
        if (size++ > 0 && IsWindowVisible(hWnd))
        {
            var sb = new StringBuilder(size);
            GetWindowText(hWnd, sb, size);

            var threadID = GetWindowThreadProcessId(hWnd, out var processID);
            var s = System.Diagnostics.Process.GetProcessById((int)processID).ProcessName;

            if (s == "explorer" && sb.ToString() != "Program Manager")
                SendMessage(hWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
        }
        return true;
    }

Upvotes: 1

Related Questions