Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EnumWindows returns closed Windows Store applications

Tags:

c#

winapi

With this code:

internal static List<DetectedWindow> EnumerateWindows()
{
    var shellWindow = GetShellWindow();

    var windows = new List<DetectedWindow>();

    EnumWindows(delegate (IntPtr handle, int lParam)
    {
        if (handle == shellWindow)
            return true;

        if (!IsWindowVisible(handle))
            return true;

        if (IsIconic(handle))
            return true;

        var length = GetWindowTextLength(handle);

        if (length == 0)
            return true;

        var builder = new StringBuilder(length);

        GetWindowText(handle, builder, length + 1);
        GetWindowRect(handle, out Rect rect);

        windows.Add(new DetectedWindow(handle, rect.ToRect(), builder.ToString()));

        return true;
    }, IntPtr.Zero);

    return windows;
}

Auxiliar class:

public class DetectedWindow
{
    public IntPtr Handle { get; private set; }

    public Rect Bounds { get; private set; }

    public string Name { get; private set; }

    public DetectedWindow(IntPtr handle, Rect bounds, string name)
    {
        Handle = handle;
        Bounds = bounds;
        Name = name;
    }
}

I'm getting this list of applications (Window text - Rect bounds):

Microsoft Visual Studio  - -8;-8;1936;1056
Microsoft Edge - 0;77;1920;963
EnumWindows - Stack Overflow and 7 more pages ‎- Microsoft Edge - -8;-8;1936;1056
Microsoft Edge - 0;77;1920;963
Microsoft Edge - 0;77;1920;963
Microsoft Edge - 0;0;1920;1080
Microsoft Edge - 0;0;1920;1080
Microsoft Edge - 0;8;1920;1040
Microsoft Edge - 0;85;1920;963
Microsoft Edge - 150;79;1532;42
Microsoft Edge - 0;85;1920;963
Microsoft Edge - 0;77;1920;963
Microsoft Edge - 0;85;1920;963
Microsoft Edge - 0;213;1920;964
Microsoft Edge - 0;0;1920;1080
Microsoft Edge - 484;208;952;174
Microsoft Edge - 0;84;1920;964
Microsoft Edge - 0;84;1920;964
Microsoft Edge - 0;84;1920;964 
Microsoft Edge - 0;0;1920;1080
Mail - 0;32;1356;693
Mail - 278;252;1372;733
OneNote - 0;8;1920;1040
My notes - OneNote - -8;-8;1936;1056
Photos - 0;32;1920;1008
Photos - -8;-8;1936;1056
Skype - 0;40;1920;1008
Skype - -8;-8;1936;1056
Store - 0;40;1920;1008
Store - -8;-8;1936;1056
Movies & TV - 0;0;1920;1080
Movies & TV - -8;-8;1936;1056
Groove Music - 0;32;1466;712
Groove Music - -7;3;1372;733
Settings - 0;40;1920;1008
Settings - -8;-8;1936;1056
Windows Shell Experience Host - 0;0;1920;1080

My current not minimized windows are Visual Studio and two Edge windows (with several tabs each). I can understand the fact that only one Edge item was listing the title of the current page. Because I recently recovered from a crash and only that page was loaded.

My questions are:

  1. Why are my closed Windows Store apps being listed? (and even twice)
  2. Why are my Edge tabs being listed?
  3. How can I filter the Edge tabs and the closed Windows Store apps?

EDIT:

  1. By "Filter": Only retrieve the apps with visible window. With my use case, only 3 windows are visible.

I tried to get the WsStyle and WsEXStyle of each window to compare, but I couldn't find any difference.

The method IsWindowVisible() fails filter out the Windows Store apps that are not visible.

like image 421
Nicke Manarin Avatar asked May 11 '17 23:05

Nicke Manarin


2 Answers

Why are my closed Windows Store apps being listed?

Because they are not actually closed. Easy to see with Task Manager, Processes tab. You'll see that the process that owns these windows is suspended. Part of the WinRT (aka UWP, aka Store, aka Modern UI, aka Metro) programming framework approach, modern machines have enough RAM to make it feasible to keep processes running even if the user doesn't interact with them. Brings them back quickly again and saves battery life. If RAM is needed elsewhere then the OS will salvage it by killing such a process.

Why are my Edge tabs being listed?

Because Edge is a WinRT app as well.

How can I filter the Edge tabs and the closed Windows Store apps?

It is not entirely clear by which property you want to filter, given that the window is in fact not closed. GetWindowThreadProcessId() and IsImmersiveProcess() can tell you that you are dealing with such a process. Consider IsWindowVisible(). Maybe this post can help, also tells you why you see multiple windows.


Edit (Nicke Manarin):

By checking the Cloacked attribute, it is possible to ignore the hidden/background Store apps:

DwmGetWindowAttribute(handle, (int)DwmWindowAttribute.Cloaked, out bool isCloacked, Marshal.SizeOf(typeof(bool)));

if (isCloacked)
    return true;

Edit 2 (Nicke Manarin):

Each Edge tab behaves like a window (I believe it has something to do with the fact that you can drag the tab to create a new window).

like image 83
5 revs, 2 users 50% Avatar answered Oct 06 '22 22:10

5 revs, 2 users 50%


I can't reproduce the behavior in my W10 Desktop. After using your code, also filtering out the WsEXStyle WS_EX_TOOLWINDOW, shows the same apps than alt+tab does. I opened and closed Edge and Photos and they didn't appear when closed anymore. Maybe it was tinkering with P/Invoke that triggered that behavior. Does it continue with the code below after a restart?.

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace ConsoleApp1
{

    public class EnumerateWindowsTest
    {

        [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")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool GetWindowRect(IntPtr hWnd, out Rect lpRect);

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


        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern int GetWindowTextLength(IntPtr hWnd);
        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
        public delegate bool EnumWindowsProc(IntPtr hwnd, IntPtr lParam);
        [DllImport("user32.dll")]
        static extern int EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool IsWindowVisible(IntPtr hWnd);
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool IsIconic(IntPtr hWnd);
        [DllImport("user32.dll", SetLastError = true)]
        static extern System.UInt32 GetWindowLong(IntPtr hWnd, int nIndex);


        internal static List<DetectedWindow> EnumerateWindows()
        {
            var shellWindow = GetShellWindow();
            var windows = new List<DetectedWindow>();
            EnumWindows(delegate (IntPtr handle, IntPtr lParam)
            {

                if (handle == shellWindow)
                    return true;

                if (!IsWindowVisible(handle))
                    return true;

                if (IsIconic(handle))
                    return true;

                if (HasSomeExtendedWindowsStyles(handle))
                    return true;

                var length = GetWindowTextLength(handle);

                if (length == 0)
                    return true;

                var builder = new StringBuilder(length);

                GetWindowText(handle, builder, length + 1);
                GetWindowRect(handle, out Rect rect);
                windows.Add(new DetectedWindow(handle, rect, builder.ToString()));

                return true;
            }, IntPtr.Zero);

            return windows;
        }

        static bool HasSomeExtendedWindowsStyles(IntPtr hwnd)
        {
            const int GWL_EXSTYLE = -20;
            const uint WS_EX_TOOLWINDOW = 0x00000080U;

            uint i = GetWindowLong(hwnd, GWL_EXSTYLE);
            if ((i & (WS_EX_TOOLWINDOW)) != 0)
            {
                return true;
            }

            return false;
        }

    }

    public class DetectedWindow
    {
        public IntPtr Handle { get; private set; }

        public EnumerateWindowsTest.Rect Bounds { get; private set; }

        public string Name { get; private set; }

        public DetectedWindow(IntPtr handle, EnumerateWindowsTest.Rect bounds, string name)
        {
            Handle = handle;
            Bounds = bounds;
            Name = name;
        }
    }



    class Program
    {

        static void DetectWindows()
        {
            foreach (DetectedWindow w in EnumerateWindowsTest.EnumerateWindows())
            {
                Console.WriteLine("{0} - {1};{2};{3};{4}",w.Name,w.Bounds.Left,w.Bounds.Top,w.Bounds.Right,w.Bounds.Bottom);
            }
        }

        static void Main(string[] args)
        {
            DetectWindows();
            Console.ReadLine();
        }
    }


}
like image 42
xyq.384.b Avatar answered Oct 06 '22 21:10

xyq.384.b