Skip to content

Commit

Permalink
#36: Show the Windows 10 Apps name at least when they are not minimiz…
Browse files Browse the repository at this point in the history
…ed, and cleanup Edge windows display
  • Loading branch information
christianrondeau committed Oct 4, 2015
1 parent f043147 commit 923e899
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 44 deletions.
3 changes: 1 addition & 2 deletions GoToWindow.Api.Tests/WindowEntryFactoryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,11 @@ public void GetGetWindowEntry_FromTestWindow()
var expectedWindowHandle = app.Process.MainWindowHandle;
var window = WindowEntryFactory.Create(expectedWindowHandle);


Assert.AreEqual(expectedWindowHandle, window.HWnd);
Assert.AreEqual((uint)app.Process.Id, window.ProcessId);
Assert.AreEqual(app.Process.ProcessName, window.ProcessName);
Assert.AreEqual(app.ExpectedWindow.Title, window.Title);
Assert.AreNotEqual(IntPtr.Zero, window.IconHandle);
Assert.IsNull(window.ProcessName);
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion GoToWindow.Api/IWindowEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ public interface IWindowEntry
IntPtr HWnd { get; set; }
uint ProcessId { get; set; }
string ProcessName { get; set; }
//TODO: Windows 10 App Icons
//string ProcessFileName { get; set; }
string Title { get; set; }
IntPtr IconHandle { get; set; }
bool IsVisible { get; set; }
bool IsVisible { get; set; }

bool Focus();
bool IsForeground();
Expand Down
9 changes: 7 additions & 2 deletions GoToWindow.Api/WindowEntryFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,16 @@ public static class WindowEntryFactory

public static WindowEntry Create(IntPtr hWnd)
{
var windowTitle = GetWindowTitle(hWnd);

uint processId;
GetWindowThreadProcessId(hWnd, out processId);

return Create(hWnd, processId);
}

public static WindowEntry Create(IntPtr hWnd, uint processId)
{
var windowTitle = GetWindowTitle(hWnd);

var iconHandle = WindowIcon.GetAppIcon(hWnd);
var isVisible = !IsIconic(hWnd);

Expand Down
169 changes: 130 additions & 39 deletions GoToWindow.Api/WindowsListFactory.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using log4net;
Expand All @@ -9,13 +10,15 @@ namespace GoToWindow.Api
{
/// <remarks>
/// Thanks to Tommy Carlier for how to get the list of windows: http://blog.tcx.be/2006/05/getting-list-of-all-open-windows.html
/// Thanks to taby for window eligibility: http://stackoverflow.com/questions/210504/enumerate-windows-like-alt-tab-does
/// Thanks to Hans Passant & Tim Beaudet for Windows 10 apps process name: http://stackoverflow.com/a/32513438/154480
/// </remarks>
public static class WindowsListFactory
{
private static readonly ILog Log = LogManager.GetLogger(typeof(WindowsListFactory).Assembly, "GoToWindow");
private const int MaxLastActivePopupIterations = 50;

delegate bool EnumWindowsProc(IntPtr hWnd, int lParam);
delegate bool EnumWindowsProc(IntPtr hWnd, int lParam);

public enum GetAncestorFlags
{
Expand All @@ -42,43 +45,131 @@ public enum GetAncestorFlags
[DllImport("user32.dll")]
static extern IntPtr GetLastActivePopup(IntPtr hWnd);

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam);

[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

public static WindowsList Load()
{
var lShellWindow = GetShellWindow();
var windows = new List<IWindowEntry>();
var currentProcessId = Process.GetCurrentProcess().Id;

EnumWindows((hWnd, lParam) =>
{
if (!HWndEligibleForActivation(hWnd, lShellWindow))
return true;
EnumWindows((hWnd, lParam) =>
{
InspectPotentialWindow(hWnd, lShellWindow, currentProcessId, windows);
return true;
}, 0);

var className = GetClassName(hWnd);
return new WindowsList(windows);
}

if (!ClassEligibleForActivation(className))
return true;
private static void InspectPotentialWindow(IntPtr hWnd, IntPtr lShellWindow, int currentProcessId, ICollection<IWindowEntry> windows)
{
if (!HWndEligibleForActivation(hWnd, lShellWindow))
return;

var window = WindowEntryFactory.Create(hWnd);
var className = GetClassName(hWnd);

if (window == null || window.ProcessId == currentProcessId || window.Title == null)
return true;
if (className == "ApplicationFrameWindow")
InspectWindows10AppWindow(hWnd, windows, className);
else
InspectNormalWindow(hWnd, currentProcessId, windows, className);
}

window.ProcessName = GetProcessName(window);
private static void InspectNormalWindow(IntPtr hWnd, int currentProcessId, ICollection<IWindowEntry> windows, string className)
{
if (!ClassEligibleForActivation(className))
return;

#if(DEBUG)
Log.DebugFormat("Found Window: {0} {1} Class: '{2}' Title: '{3}'", window.ProcessId, window.ProcessName, className, window.Title);
#endif
var window = WindowEntryFactory.Create(hWnd);

if (IsKnownException(window))
return true;
if (IsKnownException(window))
return;

windows.Add(window);
if (window.ProcessId == currentProcessId || window.Title == null)
return;

return true;
}, 0);
UpdateProcessName(window);
windows.Add(window);
LogDebugWindow("- Found normal window: ", window, className);
}

return new WindowsList(windows);
}
private static void InspectWindows10AppWindow(IntPtr hWnd, ICollection<IWindowEntry> windows, string className)
{
Log.Debug("- Found Window 10 App");

var foundChildren = false;
uint processId;
GetWindowThreadProcessId(hWnd, out processId);

EnumChildWindows(hWnd, (childHWnd, lparam) =>
{
uint childProcessId;
GetWindowThreadProcessId(childHWnd, out childProcessId);
Log.Debug(" - Checking process: " + childProcessId);
if (processId != childProcessId)
{
var childClassName = GetClassName(hWnd);

var window = WindowEntryFactory.Create(childHWnd, childProcessId);
if (window.Title != null)
{
UpdateProcessName(window);

if (IsKnownWindows10Exception(window)) return true;

//TODO: Windows 10 App Icons
// 1. Get the window.ProcessFileName
// 2. Look in the folder for AppManifest.xml
// 3. Look for Package/Properties/Logo
// 4. Load that file (should be a PNG)

windows.Add(window);
foundChildren = true;
LogDebugWindow(" - Found window: ", window, childClassName);
}
}
return true;
}, IntPtr.Zero);

if (!foundChildren)
{
var window = WindowEntryFactory.Create(hWnd, processId);
if (window.Title != null)
{
window.ProcessName = "Windows 10 App";
windows.Add(window);
LogDebugWindow(" - No windows found: ", window, className);
}
}
}

private static bool IsKnownWindows10Exception(WindowEntry window)
{
if (window.ProcessName == "MicrosoftEdge")
return true;

if (window.ProcessName == "MicrosoftEdgeCP")
{
if (window.Title == "CoreInput")
return true;

if (window.Title == "about:tabs")
return true;
}

return false;
}

[Conditional("DEBUG")]
private static void LogDebugWindow(string message, WindowEntry window, string className)
{
Log.Debug(message + window + ", Class: " + className);
}

private static bool ClassEligibleForActivation(string className)
{
Expand All @@ -98,25 +189,26 @@ private static string GetClassName(IntPtr hWnd)
return length == 0 ? null : classNameStringBuilder.ToString();
}

private static string GetProcessName(IWindowEntry window)
private static void UpdateProcessName(IWindowEntry window)
{
var processName = WmiProcessWatcher.GetProcessName(window.ProcessId, () => window.ProcessName);

if (processName == null)
window.ProcessName = WmiProcessWatcher.GetProcessName(window.ProcessId, () =>
{
using (var process = Process.GetProcessById((int) window.ProcessId))
{
processName = process.ProcessName;
//TODO: Windows 10 App Icons
/*
try
{
window.ProcessFileName = process.MainModule.FileName;
}
catch (Exception ex)
{
Log.Warn("Could not get the executable name of the process " + window, ex);
}
* */
return process.ProcessName;
}
}

if ("WWAHost".Equals(processName, StringComparison.OrdinalIgnoreCase))
return "Windows Store App";

if ("ApplicationFrameHost".Equals(processName, StringComparison.OrdinalIgnoreCase))
return "Windows Store App";

return processName;
});
}

private static bool IsKnownException(IWindowEntry window)
Expand All @@ -134,14 +226,13 @@ private static bool IsKnownException(IWindowEntry window)
"MsgrIMEWindowClass", // Messenger
"SysShadow", // Messenger
"Button", // UI component, e.g. Start Menu button
"Windows.UI.Core.CoreWindow", // Windows 10 Store Apps when minimized
"Windows.UI.Core.CoreWindow", // Windows 10 Store Apps
"Frame Alternate Owner", // Edge
"MultitaskingViewFrame", // The original Win + Tab view
};

private static bool HWndEligibleForActivation(IntPtr hWnd, IntPtr lShellWindow)
{
// http://stackoverflow.com/questions/210504/enumerate-windows-like-alt-tab-does

if (hWnd == lShellWindow)
return false;

Expand Down
1 change: 1 addition & 0 deletions GoToWindow/GoToWindow.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
Expand Down

0 comments on commit 923e899

Please sign in to comment.