Saturday, May 20, 2017

EnumWindows returns closed Windows Store applications

Leave a Comment

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.

2 Answers

Answers 1

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:

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; 

Answers 2

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();         }     }   } 
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment