Click here to Skip to main content
15,908,674 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hello!
I see 4 windows on the monitor (for example), but EnumWindows gives me more than a dozen - some incomprehensible windows that are not on the screen at all. They names are something like "Microsoft Text Input Application, Microsoft Store, etc"
IsVisible returns true, IsIconic returns false, GetWindowsRect return some rect with non-zero width and heights. So, how I can filter them ?? For now all what I can do - to get image with PrintWindow and to see it is totally black, but this way looks like a gag not solution :)

My task is simple - to get screenshot for all windows on the screen one by one, but it is not very good for me to get additional 10 black rectangles.


To be more informative: OS - Windows10

What I have tried:

class OpenWindowsGetter
{
    [DllImport("USER32.DLL")]
    static extern bool EnumWindows(EnumWindowsProc enumFunc, int lParam);
    [DllImport("USER32.DLL")]
    static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
    [DllImport("USER32.DLL")]
    static extern int GetWindowTextLength(IntPtr hWnd);
    [DllImport("USER32.DLL")]
    static extern bool IsWindowVisible(IntPtr hWnd);
    [DllImport("USER32.DLL")]
    static extern IntPtr GetShellWindow();
    [DllImport("User32.dll")]
    static extern bool IsIconic(IntPtr hwnd);

    delegate bool EnumWindowsProc(IntPtr hWnd, int lParam);
        /// <summary>Returns a dictionary that contains the handle and title of all opened windows.</summary>
        /// <returns>A dictionary that contains the handle and title of all opened windows.</returns>
        public static IDictionary<IntPtr, string> GetOpenWindows()
        {
            IntPtr lShellWindow = GetShellWindow();
            Dictionary<IntPtr, string> lWindows = new Dictionary<IntPtr, string>();

            EnumWindows(delegate (IntPtr hWnd, int lParam)
            {
                if (hWnd == lShellWindow) return true;
                if (!IsWindowVisible(hWnd)) return true;
                if (IsIconic(hWnd)) return true;

                int lLength = GetWindowTextLength(hWnd);
                if (lLength == 0) return true;

                StringBuilder lBuilder = new StringBuilder(lLength);
                GetWindowText(hWnd, lBuilder, lLength + 1);

                lWindows[hWnd] = lBuilder.ToString();
                return true;

            }, 0);

            return lWindows;
        }
}


And to see a result with combobox WindsCB:

private void button1_Click(object sender, EventArgs e)
{
    WindsCB.Items.Clear();
    IDictionary<IntPtr, string> winds = OpenWindowsGetter.GetOpenWindows();
    foreach (var item in winds) WindsCB.Items.Add(item);
}
Posted
Updated 14-Nov-22 9:08am
v3
Comments
BillWoodruff 25-May-21 10:57am    
for the "black rectangles" you describe: is there any common feature you can isolate (size ?) that would let you exclude them ?
Михаил Солодков 25-May-21 15:31pm    
Size is not common for this rectangles :\ For now I get an image and check if it is totally black ( 0-0-0 in RGB ). Well, and if GetWindowText returns "Microsoft Text Input Application" - I pass it without checking :)) But all this looks like workaround not solution.
BillWoodruff 26-May-21 2:21am    
Wish I could be more helpful !

 
Share this answer
 
Comments
Михаил Солодков 24-May-21 5:11am    
I tried it, but the result is exactly the same.
I used EnumDesktopWindows(0, delegate (IntPtr hWnd, int lParam) {..}, 0).
May be I miss something? May be not zero as desktop handle, but what else? THanks!
Richard MacCutchan 24-May-21 5:27am    
I do not think there is much you can do. The system itself uses its own Windows for various purposes so you will always get them returned. You can only work on the information provided from the Window's handle.

And the remarks section for the IsWindowVisible function (winuser.h) - Win32 apps | Microsoft Docs[^] function states:
The visibility state of a window is indicated by the WS_VISIBLE style bit. When WS_VISIBLE is set, the window is displayed and subsequent drawing into it is displayed as long as the window has the WS_VISIBLE style.

Any drawing to a window with the WS_VISIBLE style will not be displayed if the window is obscured by other windows or is clipped by its parent window.
Михаил Солодков 24-May-21 6:12am    
About your remarks - do not know why, but with winapi's PrintWindow I get an image from obscured windows w\o problems.

public static Bitmap WindImage(IntPtr hwnd)
{
GetWindowRect(hwnd, out RECT rect);
Bitmap image = new Bitmap(rect.Right - rect.Left, rect.Bottom - rect.Top);
using (var graphics = Graphics.FromImage(image))
{
var hdcBitmap = graphics.GetHdc();
PrintWindow(hwnd, hdcBitmap, 0);
graphics.ReleaseHdc(hdcBitmap);
}
return image;
}

So my problem not in "I can not get what I want", but "I get too much!!!" LOL :))) thanks for your answers anyway!
BillWoodruff 26-May-21 2:20am    
+5
Perhaps, instead of checking all windows, you could check all processes with an existing MainWindowHandle.

Therefore, prepare your "User32" class:

[StructLayout(LayoutKind.Sequential)]
public struct RECT {
  public int Left;   // x position of upper-left corner
  public int Top;    // y position of upper-left corner
  public int Right;  // x position of lower-right corner
  public int Bottom; // y position of lower-right corner
}

[DllImport("user32.dll", CharSet=CharSet.Unicode)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string lclassName, string windowTitle);

[DllImport("user32.dll", CharSet=CharSet.Unicode, SetLastError=true)]
public static extern IntPtr GetShellWindow();

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(IntPtr hWnd, [Out] out RECT lpRect);

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool IsWindowVisible(IntPtr hWnd);


Now get all main window handles:

HashSet<IntPtr> allHandles = new HashSet<IntPtr>();

foreach (Process process in Process.GetProcesses().Where(x => x.MainWindowHandle != IntPtr.Zero)) {
  allHandles.Add(process.MainWindowHandle);
}


You also need to add one special logic to find all explorer windows:

IntPtr explorerHandle = IntPtr.Zero;
while (IntPtr.Zero != (explorerHandle = User32.FindWindowEx(IntPtr.Zero, explorerHandle, "CabinetWClass", null))) {
  allHandles.Add(explorerHandle);
}


You likely need to get some special windows you need to skip/filter out:

IntPtr hShellWnd  = User32.GetShellWindow();
IntPtr hDefView   = User32.FindWindowEx(hShellWnd,   IntPtr.Zero, "SHELLDLL_DefView", null);
IntPtr folderView = User32.FindWindowEx(hDefView,    IntPtr.Zero, "SysListView32",    null);
IntPtr taskBar    = User32.FindWindowEx(IntPtr.Zero, IntPtr.Zero, "Shell_TrayWnd",    null);

HashSet<IntPtr> ForbiddenWindows = new HashSet<IntPtr>();
ForbiddenWindows.Add(hShellWnd);
ForbiddenWindows.Add(hDefView);
ForbiddenWindows.Add(folderView);
ForbiddenWindows.Add(taskBar);


Now, filter all handles to get the relevant handles:

HashSet<IntPtr> relevantHandles = new HashSet<IntPtr>();
foreach (IntPtr handle in allHandles) {
  if (handle != IntPtr.Zero && User32.IsWindowVisible(handle) && !ForbiddenWindows.Contains(handle) && !relevantHandles.Contains(handle)) {
    User32.RECT rect       = new User32.RECT();
    bool        locChecked = User32.GetWindowRect(handle, out rect);
    Rectangle   area       = new Rectangle(rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top);

    if (locChecked && area.X > -1 && area.Width > 0 && area.Height > 0) {
      relevantHandles.Add(handle);
    }
  }
}


Furthermore, instead of checking for area.X, area.Width and area.Height you could implement a check if the window area is contained by any screens workarea. Also be aware, that the size of maximized windows can exceed the size of the workarea (and even the screen bounds when the taskbar is hidden) by 4 to every direction.
 
Share this answer
 
v2

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900