13

How can I get the list of opened of folders, enumerate through it and minimize each folder programmatically?

At times some opened folders do steal focus from the tool when jumping from one form in the application to another. Preventing this is of high priority for our client. The customers are visually impaired people, so they access the machine only via screen readers. Minimizing other windows (folders) is not at all a problem, in fact a requirement.

I tried this:

foreach (Process p in Process.GetProcessesByName("explorer"))
{
    p.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
}

As expected it did no good.

Update:

From the answers here, I tried this:

    delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);

    [DllImport("user32.dll")]
    static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);

    static IEnumerable<IntPtr> EnumerateProcessWindowHandles(int processID)
    {
        List<IntPtr> handles = new List<IntPtr>();

        EnumThreadDelegate addWindowHandle = delegate(IntPtr hWnd, IntPtr param)
        {
            handles.Add(hWnd);
            return true;
        };

        foreach (ProcessThread thread in Process.GetProcessById(processID).Threads)                              
            EnumThreadWindows(thread.Id, addWindowHandle, IntPtr.Zero);

        return handles;
    }

    const int SW_MINIMIZED = 6;

    [DllImport("user32.dll")]
    static extern int ShowWindow(IntPtr hWnd, int nCmdShow);

    private void button1_Click(object sender, EventArgs e)
    {
        foreach (IntPtr handle in EnumerateProcessWindowHandles(Process.GetProcessesByName("explorer")[0].Id))
            ShowWindow(handle, SW_MINIMIZED);
    }

This creates a whole lot of invisible explorer windows to be suddenly listed in the taksbar out of no where. I am bit noob in dealing with Windows API, so the code itself will actually help.

nawfal
  • 70,104
  • 56
  • 326
  • 368
  • You need to send a message to the window, this has to be done at Windows API level. – jimjim Feb 13 '12 at 00:18
  • @Arjang can I see some code somewhere? – nawfal Feb 13 '12 at 00:21
  • 1
    If I had the code it would have been an answer not a comment. Just wanted to be of assistance in a better google search. – jimjim Feb 13 '12 at 01:54
  • Related post - [How to Minimise other windows while your application is running C#](https://stackoverflow.com/q/46154112/465053) – RBT Jan 13 '22 at 11:57

5 Answers5

9

Please try this (the code is somewhat messy but for the purpose you should be able to go through it ;))

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Text;
using System.Globalization;

namespace WindowsFormsApplication20
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            foreach (IntPtr handle in EnumerateProcessWindowHandles(Process.GetProcessesByName("explorer")[0].Id))
            {
                SendMessage(handle, WM_SYSCOMMAND, SC_MINIMIZE, 0);
            }
        }

        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);

        static string GetDaClassName(IntPtr hWnd)
        {
            int nRet;
            StringBuilder ClassName = new StringBuilder(100);
            //Get the window class name
            nRet = GetClassName(hWnd, ClassName, ClassName.Capacity);
            if (nRet != 0)
            {
                return ClassName.ToString();
            }
            else
            {
                return null;
            }
        }

        delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);

        [DllImport("user32.dll")]
        static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);

        static IEnumerable<IntPtr> EnumerateProcessWindowHandles(int processID)
        {
            List<IntPtr> handles = new List<IntPtr>();

            EnumThreadDelegate addWindowHandle = delegate(IntPtr hWnd, IntPtr param)
            {
                string className = GetDaClassName(hWnd);

                switch (className)
                {
                    case null:
                        break;
                    case "ExploreWClass":
                        handles.Add(hWnd);
                        break;
                    case "CabinetWClass":
                        handles.Add(hWnd);
                        break;
                    default:
                        break;
                }

                return true;
            };

            foreach (ProcessThread thread in Process.GetProcessById(processID).Threads)
                EnumThreadWindows(thread.Id, addWindowHandle, IntPtr.Zero);

            return handles;
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam);

        const int WM_SYSCOMMAND = 274;
        const int SC_MINIMIZE = 0xF020;
    }
}

Best regards,

Żubrówka

Żubrówka
  • 730
  • 1
  • 10
  • 24
  • Let me try that one! Thanks :) – nawfal Feb 24 '12 at 16:42
  • Before I award you the bounty, let me ask you, what should be done to minimize other open windows, say all MS Excel windows (using the very same shell API? – nawfal Feb 24 '12 at 16:49
  • Well, if you know specifically what would you like to minimize (e.g. Excel), you can get all instances of excel.exe, get the handle of the main window (if you have a Process object you can find it in the MainWindowHandle property) and just send a message to that handle requesting the window to minimize (you can use the SendMessage(handle, WM_SYSCOMMAND, SC_MINIMIZE, 0); function if you like it). – Żubrówka Feb 24 '12 at 16:54
  • If what you want is minimize everything but your program, it can be trickier, you need to enumerate all windows without a parent/parent is the desktop (i might be a bit incorrect here but i can confirm) and send the minimize message, in general it should work but there might be some more tricky parts, such as with explorer.exe (for this particular case, you had to filter handles that didn't matter (some of them reacted bad to the minimize message), I did it by filtering the class name and just taking the ExploreWClass and CabinetWClass windows) – Żubrówka Feb 24 '12 at 16:58
  • Yes, I get that. Thanks. Let me test that. – nawfal Feb 24 '12 at 16:58
  • Indeed ExploreWClass and CabinetWClass are the key here which I never knew of. And no I do not want to minimize everything but my application. (To specifically handle Excel I have interop automation :D). A big thanks :) – nawfal Feb 24 '12 at 17:01
  • 1
    GetDaClassName function is the key here. It gives the exact handle to be minimized! And yes, I can minimize any application I wish hence. Work of a genius, take my 100 points bro (in 13 hours) ! ;) – nawfal Feb 24 '12 at 17:25
5

//Create Instance Of Shell Class by referencing COM Library "Microsoft Shell Controls And Automation" -shell32.dll

Shell32.ShellClass objShell = new Shell32.ShellClass();
//Show Desktop
((Shell32.IShellDispatch4)objShell).ToggleDesktop();

Edit: to show your application (Activate or Maximize/Restore) after toggling actually turned out to be quite difficult:

I tried:

Application.DoEvents();

System.Threading.Thread.Sleep(5000);

Even overriding the WndProc didn't manage to capture the event:

private const Int32 WM_SYSCOMMAND = 0x112;
        private const Int32 SC_MINIMIZE = 0xf020;
        protected override void WndProc(ref Message m)
        {
            if (m.Msg == WM_SYSCOMMAND)
            {
                if (m.WParam.ToInt32() == SC_MINIMIZE)
                    return;
            }
            base.WndProc(ref m);
        }

So I suggest instead of Minimising all other windows, just stick yours on top during the operation, then once your finished turn off Always On Top:

  [DllImport("user32.dll")]
    static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);

const UInt32 SWP_NOSIZE = 0x0001;
const UInt32 SWP_NOMOVE = 0x0002;
const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE;

public static void MakeTopMost (IntPtr hWnd)
{
    SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS);
}
Jeremy Thompson
  • 61,933
  • 36
  • 195
  • 321
  • This minimizes everything including my app. this.Activate doesn't bring it to front – nawfal Feb 13 '12 at 01:26
  • sorry it should be this.WindowState = FormWindowState.Maximized; – Jeremy Thompson Feb 13 '12 at 01:48
  • No way. it still keeps my form in the bottom :( – nawfal Feb 13 '12 at 02:10
  • @nawfal - yes, your right and this was a PITA. Please see my edited answer. I hope its a suitable workaround. – Jeremy Thompson Feb 13 '12 at 04:38
  • This doesn't still work. Sorry for the delay in reply. It minimizes all the windows to show desktop, even taking the focus from the application. Previously I used to get the focus. I do not know what is happening under the hood. – nawfal Feb 24 '12 at 07:08
3

There is a less 'hacky' solution than the accepted answer available here: Minimize a folder

It's based on the Shell Objects for Scripting. Sample:

const int SW_SHOWMINNOACTIVE = 7;

[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

static void MinimizeWindow(IntPtr handle)
{
    ShowWindow(handle, SW_SHOWMINNOACTIVE);
}

//call it like:

foreach (IWebBrowser2 window in new Shell().Windows())
{
    if (window.Name == "Windows Explorer")
        MinimizeWindow((IntPtr)window.HWND);
}

The same thing can be achieved using the Internet Explorer Object Model

// add a reference to "Microsoft Internet Controls" COM component
// also add a 'using SHDocVw;'
foreach (IWebBrowser2 window in new ShellWindows())
{
    if (window.Name == "Windows Explorer")
        MinimizeWindow((IntPtr)window.HWND);
}
Community
  • 1
  • 1
Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
  • Simon, thanks for that, it works. Also instead of `new Shell().Windows` you can call `new ShellWindows()` in `interop.SHDocVw`. One advantage of the accepted answer is that its easy to extent to other applications' windows as well, but this is simpler for the given task. I'm in two minds as to which answer to accept :) – nawfal Jan 12 '14 at 16:21
  • Can I add a small code snippet to your answer to demonstrate? Also this has a *small* problem where it takes the focus away from the app. Not that it is difficult to bring back, but that's a small glitch nevertheless. Does it happen to you? And know why would that happen? – nawfal Jan 12 '14 at 16:23
  • @nawfal - you can add a snippet if you want. It's true that this is only related to Windows explorer folder windows - that was the question :-) and I think it's much more future-proof. If you don't want to minimize, just use another ShowWindow command, probably SW_SHOWMINNOACTIVE. – Simon Mourier Jan 12 '14 at 17:35
  • Yes `SW_SHOWMINNOACTIVE` does the trick. Hope my edit is ok. Feel free to edit it further. – nawfal Jan 12 '14 at 20:46
3

If you are willing to use p-invoke you can use EnumThreadWindows to enumerate all windows of a process. Then use ShowWindow to minimize them.

rasmus
  • 3,136
  • 17
  • 22
  • Is this to enumerate through all general windows? or just folder windows? – nawfal Feb 13 '12 at 00:43
  • You can enumerate all topmost windows of a specific process using this method. In your case all windows belonging to the explorer. You need to get a handle to the main thread of the process. – rasmus Feb 13 '12 at 00:48
  • Hey ramsus can you post little bit of the code itself, I can not get to do this. – nawfal Feb 13 '12 at 00:50
  • 2
    There is [another question](http://stackoverflow.com/questions/2531828/how-to-enumerate-all-windows-belonging-to-a-particular-process-using-net) about this. – rasmus Feb 13 '12 at 00:51
0

I know this is an old post, but here is a much shorter, simpler way in case people are still looking for a solution:

Using Windows API:

Declare a windows handle: (minimizes the calling executable)

HWND wHandle;  //can be any scope - I use it in main 

Call the following anywhere (depending on scope of wHandle):

wHandle = GetActiveWindow(); 
ShowWindow(wHandle, SW_SHOWMINNOACTIVE);
ryyker
  • 22,849
  • 3
  • 43
  • 87