diff --git a/Greenshot/Forms/MainForm.cs b/Greenshot/Forms/MainForm.cs
index ccb6b9a4f..fb35500b6 100644
--- a/Greenshot/Forms/MainForm.cs
+++ b/Greenshot/Forms/MainForm.cs
@@ -918,7 +918,7 @@ namespace Greenshot.Forms {
private void ShowThumbnailOnEnter(object sender, EventArgs e) {
if (sender is not ToolStripMenuItem captureWindowItem) return;
- WindowDetails window = captureWindowItem.Tag as WindowDetails;
+ var window = captureWindowItem.Tag as WindowDetails;
if (_thumbnailForm == null) {
_thumbnailForm = new ThumbnailForm();
}
@@ -939,29 +939,39 @@ namespace Greenshot.Forms {
_thumbnailForm = null;
}
+ ///
+ /// Create the "capture window from list" list
+ ///
+ /// ToolStripMenuItem
+ /// EventHandler
public void AddCaptureWindowMenuItems(ToolStripMenuItem menuItem, EventHandler eventHandler) {
menuItem.DropDownItems.Clear();
// check if thumbnailPreview is enabled and DWM is enabled
bool thumbnailPreview = _conf.ThumnailPreview && DWM.IsDwmEnabled;
- foreach(WindowDetails window in WindowDetails.GetTopLevelWindows()) {
-
+ foreach(var window in WindowDetails.GetTopLevelWindows()) {
+ if (LOG.IsDebugEnabled)
+ {
+ LOG.Debug(window.ToString());
+ }
string title = window.Text;
- if (title != null) {
- if (title.Length > _conf.MaxMenuItemLength) {
- title = title.Substring(0, Math.Min(title.Length, _conf.MaxMenuItemLength));
- }
- ToolStripItem captureWindowItem = menuItem.DropDownItems.Add(title);
- captureWindowItem.Tag = window;
- captureWindowItem.Image = window.DisplayIcon;
- captureWindowItem.Click += eventHandler;
- // Only show preview when enabled
- if (thumbnailPreview) {
- captureWindowItem.MouseEnter += ShowThumbnailOnEnter;
- captureWindowItem.MouseLeave += HideThumbnailOnLeave;
- }
- }
- }
+ if (string.IsNullOrEmpty(title))
+ {
+ continue;
+ }
+ if (title.Length > _conf.MaxMenuItemLength) {
+ title = title.Substring(0, Math.Min(title.Length, _conf.MaxMenuItemLength));
+ }
+ ToolStripItem captureWindowItem = menuItem.DropDownItems.Add(title);
+ captureWindowItem.Tag = window;
+ captureWindowItem.Image = window.DisplayIcon;
+ captureWindowItem.Click += eventHandler;
+ // Only show preview when enabled
+ if (thumbnailPreview) {
+ captureWindowItem.MouseEnter += ShowThumbnailOnEnter;
+ captureWindowItem.MouseLeave += HideThumbnailOnLeave;
+ }
+ }
}
private void CaptureAreaToolStripMenuItemClick(object sender, EventArgs e) {
diff --git a/Greenshot/Helpers/CaptureHelper.cs b/Greenshot/Helpers/CaptureHelper.cs
index 19acda012..813a18b4f 100644
--- a/Greenshot/Helpers/CaptureHelper.cs
+++ b/Greenshot/Helpers/CaptureHelper.cs
@@ -970,7 +970,7 @@ namespace Greenshot.Helpers {
// The following, to be precise the HideApp, causes the app to close as described in BUG-1620
// Added check for metro (Modern UI) apps, which might be maximized and cover the screen.
- //foreach(WindowDetails app in WindowDetails.GetMetroApps()) {
+ //foreach(WindowDetails app in WindowDetails.GetAppWindows()) {
// if (app.Maximised) {
// app.HideApp();
// }
diff --git a/GreenshotPlugin/Controls/ThumbnailForm.cs b/GreenshotPlugin/Controls/ThumbnailForm.cs
index 3578b63d3..aae16b46c 100644
--- a/GreenshotPlugin/Controls/ThumbnailForm.cs
+++ b/GreenshotPlugin/Controls/ThumbnailForm.cs
@@ -22,6 +22,7 @@ using System;
using System.Windows.Forms;
using GreenshotPlugin.Core;
using System.Drawing;
+using GreenshotPlugin.Core.Enums;
using GreenshotPlugin.IniFile;
using GreenshotPlugin.UnmanagedHelpers;
using GreenshotPlugin.UnmanagedHelpers.Enums;
@@ -32,7 +33,7 @@ namespace GreenshotPlugin.Controls {
/// This form allows us to show a Thumbnail preview of a window near the context menu when selecting a window to capture.
/// Didn't make it completely "generic" yet, but at least most logic is in here so we don't have it in the mainform.
///
- public class ThumbnailForm : FormWithoutActivation {
+ public sealed class ThumbnailForm : FormWithoutActivation {
private static readonly CoreConfiguration conf = IniConfig.GetIniSection();
private IntPtr _thumbnailHandle = IntPtr.Zero;
@@ -59,12 +60,13 @@ namespace GreenshotPlugin.Controls {
base.Hide();
}
- private void UnregisterThumbnail() {
- if (_thumbnailHandle != IntPtr.Zero) {
- DWM.DwmUnregisterThumbnail(_thumbnailHandle);
- _thumbnailHandle = IntPtr.Zero;
- }
- }
+ private void UnregisterThumbnail()
+ {
+ if (_thumbnailHandle == IntPtr.Zero) return;
+
+ DWM.DwmUnregisterThumbnail(_thumbnailHandle);
+ _thumbnailHandle = IntPtr.Zero;
+ }
///
/// Show the thumbnail of the supplied window above (or under) the parent Control
@@ -75,41 +77,60 @@ namespace GreenshotPlugin.Controls {
UnregisterThumbnail();
DWM.DwmRegisterThumbnail(Handle, window.Handle, out _thumbnailHandle);
- if (_thumbnailHandle != IntPtr.Zero) {
- DWM.DwmQueryThumbnailSourceSize(_thumbnailHandle, out var sourceSize);
- int thumbnailHeight = 200;
- int thumbnailWidth = (int)(thumbnailHeight * (sourceSize.Width / (float)sourceSize.Height));
- if (parentControl != null && thumbnailWidth > parentControl.Width) {
- thumbnailWidth = parentControl.Width;
- thumbnailHeight = (int)(thumbnailWidth * (sourceSize.Height / (float)sourceSize.Width));
- }
- Width = thumbnailWidth;
- Height = thumbnailHeight;
- // Prepare the displaying of the Thumbnail
- DWM_THUMBNAIL_PROPERTIES props = new DWM_THUMBNAIL_PROPERTIES
- {
- Opacity = 255,
- Visible = true,
- SourceClientAreaOnly = false,
- Destination = new RECT(0, 0, thumbnailWidth, thumbnailHeight)
- };
- DWM.DwmUpdateThumbnailProperties(_thumbnailHandle, ref props);
- if (parentControl != null) {
- AlignToControl(parentControl);
- }
+ if (_thumbnailHandle == IntPtr.Zero) return;
- if (!Visible) {
- Show();
- }
- // Make sure it's on "top"!
- if (parentControl != null) {
- User32.SetWindowPos(Handle, parentControl.Handle, 0, 0, 0, 0, WindowPos.SWP_NOMOVE | WindowPos.SWP_NOSIZE | WindowPos.SWP_NOACTIVATE);
- }
- }
- }
+ var result = DWM.DwmQueryThumbnailSourceSize(_thumbnailHandle, out var sourceSize);
+ if (result.Failed())
+ {
+ DWM.DwmUnregisterThumbnail(_thumbnailHandle);
+ return;
+ }
+
+ if (sourceSize.IsEmpty)
+ {
+ DWM.DwmUnregisterThumbnail(_thumbnailHandle);
+ return;
+ }
+
+ int thumbnailHeight = 200;
+ int thumbnailWidth = (int)(thumbnailHeight * (sourceSize.Width / (float)sourceSize.Height));
+ if (parentControl != null && thumbnailWidth > parentControl.Width)
+ {
+ thumbnailWidth = parentControl.Width;
+ thumbnailHeight = (int)(thumbnailWidth * (sourceSize.Height / (float)sourceSize.Width));
+ }
+ Width = thumbnailWidth;
+ Height = thumbnailHeight;
+ // Prepare the displaying of the Thumbnail
+ var dwmThumbnailProperties = new DWM_THUMBNAIL_PROPERTIES
+ {
+ Opacity = 255,
+ Visible = true,
+ SourceClientAreaOnly = false,
+ Destination = new RECT(0, 0, thumbnailWidth, thumbnailHeight)
+ };
+ result = DWM.DwmUpdateThumbnailProperties(_thumbnailHandle, ref dwmThumbnailProperties);
+ if (result.Failed())
+ {
+ DWM.DwmUnregisterThumbnail(_thumbnailHandle);
+ return;
+ }
+
+ if (parentControl != null) {
+ AlignToControl(parentControl);
+ }
+
+ if (!Visible) {
+ Show();
+ }
+ // Make sure it's on "top"!
+ if (parentControl != null) {
+ User32.SetWindowPos(Handle, parentControl.Handle, 0, 0, 0, 0, WindowPos.SWP_NOMOVE | WindowPos.SWP_NOSIZE | WindowPos.SWP_NOACTIVATE);
+ }
+ }
public void AlignToControl(Control alignTo) {
- Rectangle screenBounds = WindowCapture.GetScreenBounds();
+ var screenBounds = WindowCapture.GetScreenBounds();
if (screenBounds.Contains(alignTo.Left, alignTo.Top - Height)) {
Location = new Point(alignTo.Left + (alignTo.Width / 2) - (Width / 2), alignTo.Top - Height);
} else {
diff --git a/GreenshotPlugin/Core/WindowDetails.cs b/GreenshotPlugin/Core/WindowDetails.cs
index a2d3d9a63..903483c0c 100644
--- a/GreenshotPlugin/Core/WindowDetails.cs
+++ b/GreenshotPlugin/Core/WindowDetails.cs
@@ -2,7 +2,9 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
+using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
+using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
@@ -25,11 +27,11 @@ namespace GreenshotPlugin.Core
///
/// Provides details about a Window returned by the enumeration
///
- public class WindowDetails : IEquatable{
- private const string MetroWindowsClass = "Windows.UI.Core.CoreWindow"; //Used for Windows 8(.1)
- private const string FramedAppClass = "ApplicationFrameWindow"; // Windows 10 uses ApplicationFrameWindow
- private const string MetroApplauncherClass = "ImmersiveLauncher";
- private const string MetroGutterClass = "ImmersiveGutter";
+ public class WindowDetails : IEquatable {
+ private const string AppWindowClass = "Windows.UI.Core.CoreWindow"; //Used for Windows 8(.1)
+ private const string AppFrameWindowClass = "ApplicationFrameWindow"; // Windows 10 uses ApplicationFrameWindow
+ private const string ApplauncherClass = "ImmersiveLauncher";
+ private const string GutterClass = "ImmersiveGutter";
private static readonly IList IgnoreClasses = new List(new[] { "Progman", "Button", "Dwm" }); //"MS-SDIa"
private static readonly ILog Log = LogManager.GetLogger(typeof(WindowDetails));
@@ -72,23 +74,28 @@ namespace GreenshotPlugin.Core
/// This checks if the window is a Windows 8 App
/// For Windows 10 most normal code works, as it's hosted inside "ApplicationFrameWindow"
///
- public bool IsApp => MetroWindowsClass.Equals(ClassName);
+ public bool IsApp => AppWindowClass.Equals(ClassName);
///
/// This checks if the window is a Windows 10 App
/// For Windows 10 apps are hosted inside "ApplicationFrameWindow"
///
- public bool IsWin10App => FramedAppClass.Equals(ClassName);
+ public bool IsWin10App => AppFrameWindowClass.Equals(ClassName);
+ ///
+ /// Check if this window belongs to a background app
+ ///
+ public bool IsBackgroundWin10App => WindowsVersion.IsWindows10OrLater && AppFrameWindowClass.Equals(ClassName) && !Children.Any(window => string.Equals(window.ClassName, AppWindowClass));
+
///
/// Check if the window is the metro gutter (sizeable separator)
///
- public bool IsGutter => MetroGutterClass.Equals(ClassName);
+ public bool IsGutter => GutterClass.Equals(ClassName);
///
/// Test if this window is for the App-Launcher
///
- public bool IsAppLauncher => MetroApplauncherClass.Equals(ClassName);
+ public bool IsAppLauncher => ApplauncherClass.Equals(ClassName);
///
/// Check if this window is the window of a metro app
@@ -447,7 +454,7 @@ namespace GreenshotPlugin.Core
///
public bool Visible {
get {
- // Tip from Raymond Chen
+ // Tip from Raymond Chen https://devblogs.microsoft.com/oldnewthing/20200302-00/?p=103507
if (IsCloaked)
{
return false;
@@ -1337,7 +1344,7 @@ namespace GreenshotPlugin.Core
/// List WindowDetails with all the visible top level windows
public static IEnumerable GetVisibleWindows() {
Rectangle screenBounds = WindowCapture.GetScreenBounds();
- foreach(var window in GetMetroApps()) {
+ foreach(var window in GetAppWindows()) {
if (IsVisible(window, screenBounds))
{
yield return window;
@@ -1357,37 +1364,24 @@ namespace GreenshotPlugin.Core
/// These are all Windows with Classname "Windows.UI.Core.CoreWindow"
///
/// List WindowDetails with visible metro apps
- public static IEnumerable GetMetroApps() {
+ public static IEnumerable GetAppWindows() {
// if the appVisibility != null we have Windows 8.
if (AppVisibility == null)
{
yield break;
}
- //string[] wcs = {"ImmersiveGutter", "Snapped Desktop", "ImmersiveBackgroundWindow","ImmersiveLauncher","Windows.UI.Core.CoreWindow","ApplicationManager_ImmersiveShellWindow","SearchPane","MetroGhostWindow","EdgeUiInputWndClass", "NativeHWNDHost", "Shell_CharmWindow"};
- //List specials = new List();
- //foreach(string wc in wcs) {
- // IntPtr wcHandle = User32.FindWindow(null, null);
- // while (wcHandle != IntPtr.Zero) {
- // WindowDetails special = new WindowDetails(wcHandle);
- // if (special.WindowRectangle.Left >= 1920 && special.WindowRectangle.Size != Size.Empty) {
- // specials.Add(special);
- // LOG.DebugFormat("Found special {0} : {1} at {2} visible: {3} {4} {5}", special.ClassName, special.Text, special.WindowRectangle, special.Visible, special.ExtendedWindowStyle, special.WindowStyle);
- // }
- // wcHandle = User32.FindWindowEx(IntPtr.Zero, wcHandle, null, null);
- // };
- //}
- IntPtr nextHandle = User32.FindWindow(MetroWindowsClass, null);
+ var nextHandle = User32.FindWindow(AppWindowClass, null);
while (nextHandle != IntPtr.Zero) {
var metroApp = new WindowDetails(nextHandle);
yield return metroApp;
// Check if we have a gutter!
if (metroApp.Visible && !metroApp.Maximised) {
- var gutterHandle = User32.FindWindow(MetroGutterClass, null);
+ var gutterHandle = User32.FindWindow(GutterClass, null);
if (gutterHandle != IntPtr.Zero) {
yield return new WindowDetails(gutterHandle);
}
}
- nextHandle = User32.FindWindowEx(IntPtr.Zero, nextHandle, MetroWindowsClass, null);
+ nextHandle = User32.FindWindowEx(IntPtr.Zero, nextHandle, AppWindowClass, null);
}
}
@@ -1398,21 +1392,10 @@ namespace GreenshotPlugin.Core
/// bool
private static bool IsTopLevel(WindowDetails window)
{
- // Window is not on this desktop
if (window.IsCloaked)
{
return false;
}
-
- // Ignore windows without title
- if (window.Text.Length == 0)
- {
- return false;
- }
- if (IgnoreClasses.Contains(window.ClassName))
- {
- return false;
- }
// Windows without size
if (window.WindowRectangle.Size.Width * window.WindowRectangle.Size.Height == 0)
{
@@ -1437,7 +1420,21 @@ namespace GreenshotPlugin.Core
{
return false;
}
- return window.Visible || window.Iconic;
+ // Ignore windows without title
+ if (window.Text.Length == 0)
+ {
+ return false;
+ }
+ if (IgnoreClasses.Contains(window.ClassName))
+ {
+ return false;
+ }
+ if (!(window.Visible || window.Iconic))
+ {
+ return false;
+ }
+
+ return !window.IsBackgroundWin10App;
}
///
@@ -1445,7 +1442,7 @@ namespace GreenshotPlugin.Core
///
/// List WindowDetails with all the top level windows
public static IEnumerable GetTopLevelWindows() {
- foreach (var possibleTopLevel in GetMetroApps())
+ foreach (var possibleTopLevel in GetAppWindows())
{
if (IsTopLevel(possibleTopLevel))
{
@@ -1523,7 +1520,7 @@ namespace GreenshotPlugin.Core
if (AppVisibility == null) {
return null;
}
- IntPtr appLauncher = User32.FindWindow(MetroApplauncherClass, null);
+ IntPtr appLauncher = User32.FindWindow(ApplauncherClass, null);
if (appLauncher != IntPtr.Zero) {
return new WindowDetails (appLauncher);
}
@@ -1542,5 +1539,32 @@ namespace GreenshotPlugin.Core
return false;
}
}
+
+ ///
+ /// Make a string representation of the window details
+ ///
+ /// string
+ public override string ToString()
+ {
+ var result = new StringBuilder();
+ result.AppendLine($"Text: {Text}");
+ result.AppendLine($"ClassName: {ClassName}");
+ result.AppendLine($"ExtendedWindowStyle: {ExtendedWindowStyle}");
+ result.AppendLine($"WindowStyle: {WindowStyle}");
+ result.AppendLine($"Size: {WindowRectangle.Size}");
+ result.AppendLine($"HasParent: {HasParent}");
+ result.AppendLine($"IsWin10App: {IsWin10App}");
+ result.AppendLine($"IsApp: {IsApp}");
+ result.AppendLine($"Visible: {Visible}");
+ result.AppendLine($"IsWindowVisible: {User32.IsWindowVisible(Handle)}");
+ result.AppendLine($"IsCloaked: {IsCloaked}");
+ result.AppendLine($"Iconic: {Iconic}");
+ result.AppendLine($"IsBackgroundWin10App: {IsBackgroundWin10App}");
+ if (HasChildren)
+ {
+ result.AppendLine($"Children classes: {string.Join(",", Children.Select(c => c.ClassName))}");
+ }
+ return result.ToString();
+ }
}
}
\ No newline at end of file
diff --git a/GreenshotPlugin/UnmanagedHelpers/DWM.cs b/GreenshotPlugin/UnmanagedHelpers/DWM.cs
index b95a766ba..eed7c71e5 100644
--- a/GreenshotPlugin/UnmanagedHelpers/DWM.cs
+++ b/GreenshotPlugin/UnmanagedHelpers/DWM.cs
@@ -23,6 +23,7 @@ using System;
using System.Drawing;
using System.Runtime.InteropServices;
using GreenshotPlugin.Core;
+using GreenshotPlugin.Core.Enums;
using GreenshotPlugin.UnmanagedHelpers.Enums;
using GreenshotPlugin.UnmanagedHelpers.Structs;
using Microsoft.Win32;
@@ -40,9 +41,9 @@ namespace GreenshotPlugin.UnmanagedHelpers {
[DllImport("dwmapi", SetLastError = true)]
public static extern int DwmUnregisterThumbnail(IntPtr thumb);
[DllImport("dwmapi", SetLastError = true)]
- public static extern int DwmQueryThumbnailSourceSize(IntPtr thumb, out SIZE size);
+ public static extern HResult DwmQueryThumbnailSourceSize(IntPtr thumb, out SIZE size);
[DllImport("dwmapi", SetLastError = true)]
- public static extern int DwmUpdateThumbnailProperties(IntPtr hThumb, ref DWM_THUMBNAIL_PROPERTIES props);
+ public static extern HResult DwmUpdateThumbnailProperties(IntPtr hThumb, ref DWM_THUMBNAIL_PROPERTIES props);
// Deprecated as of Windows 8 Release Preview
[DllImport("dwmapi", SetLastError = true)]
diff --git a/GreenshotPlugin/UnmanagedHelpers/Enums/DWMWINDOWATTRIBUTE.cs b/GreenshotPlugin/UnmanagedHelpers/Enums/DWMWINDOWATTRIBUTE.cs
index 04490cd17..e58bde613 100644
--- a/GreenshotPlugin/UnmanagedHelpers/Enums/DWMWINDOWATTRIBUTE.cs
+++ b/GreenshotPlugin/UnmanagedHelpers/Enums/DWMWINDOWATTRIBUTE.cs
@@ -26,7 +26,21 @@ namespace GreenshotPlugin.UnmanagedHelpers.Enums
[SuppressMessage("ReSharper", "InconsistentNaming")]
public enum DWMWINDOWATTRIBUTE : uint
{
+ DWMWA_NCRENDERING_ENABLED = 1,
+ DWMWA_NCRENDERING_POLICY,
+ DWMWA_TRANSITIONS_FORCEDISABLED,
+ DWMWA_ALLOW_NCPAINT,
+ DWMWA_CAPTION_BUTTON_BOUNDS,
+ DWMWA_NONCLIENT_RTL_LAYOUT,
+ DWMWA_FORCE_ICONIC_REPRESENTATION,
+ DWMWA_FLIP3D_POLICY,
DWMWA_EXTENDED_FRAME_BOUNDS, // This is the one we need for retrieving the Window size since Windows Vista
+ DWMWA_HAS_ICONIC_BITMAP, // Since Windows 7
+ DWMWA_DISALLOW_PEEK, // Since Windows 7
+ DWMWA_EXCLUDED_FROM_PEEK, // Since Windows 7
+ DWMWA_CLOAK, // Since Windows 8
DWMWA_CLOAKED, // Since Windows 8
+ DWMWA_FREEZE_REPRESENTATION, // Since Windows 8
+ DWMWA_LAST
}
}
\ No newline at end of file
diff --git a/GreenshotPlugin/UnmanagedHelpers/Structs/SIZE.cs b/GreenshotPlugin/UnmanagedHelpers/Structs/SIZE.cs
index be5d00bbc..bd1b78699 100644
--- a/GreenshotPlugin/UnmanagedHelpers/Structs/SIZE.cs
+++ b/GreenshotPlugin/UnmanagedHelpers/Structs/SIZE.cs
@@ -40,5 +40,7 @@ namespace GreenshotPlugin.UnmanagedHelpers.Structs {
public Size ToSize() {
return new Size(Width, Height);
}
+
+ public bool IsEmpty => Width * Height == 0;
}
}