From 0202c2fa8b85e8d5843723127d1fd47e4dd334a4 Mon Sep 17 00:00:00 2001 From: Robin Date: Tue, 3 Mar 2020 09:10:33 +0100 Subject: [PATCH] BUG-2553, BUG-2535: This should fix the issues we are having with selection / presenting the wrong windows. (Previous commit had a bug) --- Greenshot/Forms/CaptureForm.cs | 4 +- Greenshot/Forms/MainForm.cs | 2 +- Greenshot/Forms/SettingsForm.cs | 2 +- Greenshot/Helpers/CaptureHelper.cs | 39 +++--- GreenshotPlugin/Core/WindowDetails.cs | 117 +++++++++--------- GreenshotPlugin/Core/WindowsEnumerator.cs | 4 +- GreenshotPlugin/UnmanagedHelpers/DWM.cs | 35 +++--- .../UnmanagedHelpers/EnumWindowsProc.cs | 29 ++++- 8 files changed, 129 insertions(+), 103 deletions(-) diff --git a/Greenshot/Forms/CaptureForm.cs b/Greenshot/Forms/CaptureForm.cs index a26d113e9..dc5332708 100644 --- a/Greenshot/Forms/CaptureForm.cs +++ b/Greenshot/Forms/CaptureForm.cs @@ -97,7 +97,7 @@ namespace Greenshot.Forms { public WindowDetails SelectedCaptureWindow => _selectedCaptureWindow; /// - /// This should prevent childs to draw backgrounds + /// This should prevent children to draw backgrounds /// protected override CreateParams CreateParams { [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] @@ -119,7 +119,7 @@ namespace Greenshot.Forms { } private void ClosingHandler(object sender, EventArgs e) { - Log.Debug("Closing captureform"); + Log.Debug("Closing capture form"); WindowDetails.UnregisterIgnoreHandle(Handle); } diff --git a/Greenshot/Forms/MainForm.cs b/Greenshot/Forms/MainForm.cs index 03078804b..d9172eaa4 100644 --- a/Greenshot/Forms/MainForm.cs +++ b/Greenshot/Forms/MainForm.cs @@ -927,7 +927,7 @@ namespace Greenshot { 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(); + bool thumbnailPreview = _conf.ThumnailPreview && DWM.IsDwmEnabled; foreach(WindowDetails window in WindowDetails.GetTopLevelWindows()) { diff --git a/Greenshot/Forms/SettingsForm.cs b/Greenshot/Forms/SettingsForm.cs index d9098e7b4..90fc1df51 100644 --- a/Greenshot/Forms/SettingsForm.cs +++ b/Greenshot/Forms/SettingsForm.cs @@ -193,7 +193,7 @@ namespace Greenshot { private void SetWindowCaptureMode(WindowCaptureMode selectedWindowCaptureMode) { WindowCaptureMode[] availableModes; - if (!DWM.IsDwmEnabled()) { + if (!DWM.IsDwmEnabled) { // Remove DWM from configuration, as DWM is disabled! if (coreConfiguration.WindowCaptureMode == WindowCaptureMode.Aero || coreConfiguration.WindowCaptureMode == WindowCaptureMode.AeroTransparent) { coreConfiguration.WindowCaptureMode = WindowCaptureMode.GDI; diff --git a/Greenshot/Helpers/CaptureHelper.cs b/Greenshot/Helpers/CaptureHelper.cs index b1d7dbd3d..c2d1f2aab 100644 --- a/Greenshot/Helpers/CaptureHelper.cs +++ b/Greenshot/Helpers/CaptureHelper.cs @@ -813,7 +813,7 @@ namespace Greenshot.Helpers { Rectangle windowRectangle = windowToCapture.WindowRectangle; // When Vista & DWM (Aero) enabled - bool dwmEnabled = DWM.IsDwmEnabled(); + bool dwmEnabled = DWM.IsDwmEnabled; // get process name to be able to exclude certain processes from certain capture modes using (Process process = windowToCapture.Process) { bool isAutoMode = windowCaptureMode == WindowCaptureMode.Auto; @@ -1010,26 +1010,27 @@ namespace Greenshot.Helpers { } finally { captureForm.Hide(); } - if (result == DialogResult.OK) { - _selectedCaptureWindow = captureForm.SelectedCaptureWindow; - _captureRect = captureForm.CaptureRectangle; - // Get title - if (_selectedCaptureWindow != null) { - _capture.CaptureDetails.Title = _selectedCaptureWindow.Text; - } - if (_captureRect.Height > 0 && _captureRect.Width > 0) { - // Take the captureRect, this already is specified as bitmap coordinates - _capture.Crop(_captureRect); + if (result != DialogResult.OK) return; - // save for re-capturing later and show recapture context menu option - // Important here is that the location needs to be offsetted back to screen coordinates! - Rectangle tmpRectangle = _captureRect; - tmpRectangle.Offset(_capture.ScreenBounds.Location.X, _capture.ScreenBounds.Location.Y); - CoreConfig.LastCapturedRegion = tmpRectangle; - } - HandleCapture(); + _selectedCaptureWindow = captureForm.SelectedCaptureWindow; + _captureRect = captureForm.CaptureRectangle; + // Get title + if (_selectedCaptureWindow != null) { + _capture.CaptureDetails.Title = _selectedCaptureWindow.Text; } - } + + if (_captureRect.Height > 0 && _captureRect.Width > 0) { + // Take the captureRect, this already is specified as bitmap coordinates + _capture.Crop(_captureRect); + + // save for re-capturing later and show recapture context menu option + // Important here is that the location needs to be offsetted back to screen coordinates! + Rectangle tmpRectangle = _captureRect; + tmpRectangle.Offset(_capture.ScreenBounds.Location.X, _capture.ScreenBounds.Location.Y); + CoreConfig.LastCapturedRegion = tmpRectangle; + } + HandleCapture(); + } } } diff --git a/GreenshotPlugin/Core/WindowDetails.cs b/GreenshotPlugin/Core/WindowDetails.cs index 8f3579c4b..10f087163 100644 --- a/GreenshotPlugin/Core/WindowDetails.cs +++ b/GreenshotPlugin/Core/WindowDetails.cs @@ -553,6 +553,11 @@ namespace GreenshotPlugin.Core /// public bool Visible { get { + // Tip from Raymond Chen + if (DWM.IsWindowCloaked(Handle)) + { + return false; + } if (IsApp) { Rectangle windowRectangle = WindowRectangle; foreach (Screen screen in Screen.AllScreens) { @@ -584,8 +589,7 @@ namespace GreenshotPlugin.Core if (IsAppLauncher) { return IsAppLauncherVisible; } - // Tip from Raymond Chen - return User32.IsWindowVisible(Handle) && !DWM.IsWindowCloaked(Handle); + return User32.IsWindowVisible(Handle); } } @@ -634,55 +638,57 @@ namespace GreenshotPlugin.Core // Try to return a cached value long now = DateTime.Now.Ticks; if (_previousWindowRectangle.IsEmpty || !_frozen) { - if (_previousWindowRectangle.IsEmpty || now - _lastWindowRectangleRetrieveTime > CacheTime) { - Rectangle windowRect = Rectangle.Empty; - if (DWM.IsDwmEnabled()) + if (!_previousWindowRectangle.IsEmpty && now - _lastWindowRectangleRetrieveTime <= CacheTime) + { + return _previousWindowRectangle; + } + Rectangle windowRect = Rectangle.Empty; + if (DWM.IsDwmEnabled) + { + bool gotFrameBounds = GetExtendedFrameBounds(out windowRect); + if (IsApp) { - bool gotFrameBounds = GetExtendedFrameBounds(out windowRect); - if (IsApp) + // Pre-Cache for maximized call, this is only on Windows 8 apps (full screen) + if (gotFrameBounds) { - // Pre-Cache for Maximised call, this is only on Windows 8 apps (full screen) - if (gotFrameBounds) - { - _previousWindowRectangle = windowRect; - _lastWindowRectangleRetrieveTime = now; - } - } - if (gotFrameBounds && WindowsVersion.IsWindows10OrLater && !Maximised) - { - // Somehow DWM doesn't calculate it corectly, there is a 1 pixel border around the capture - // Remove this border, currently it's fixed but TODO: Make it depend on the OS? - windowRect.Inflate(Conf.Win10BorderCrop); _previousWindowRectangle = windowRect; _lastWindowRectangleRetrieveTime = now; - return windowRect; } } - - if (windowRect.IsEmpty) { - if (!GetWindowRect(out windowRect)) - { - Win32Error error = Win32.GetLastErrorCode(); - Log.WarnFormat("Couldn't retrieve the windows rectangle: {0}", Win32.GetMessage(error)); - } + if (gotFrameBounds && WindowsVersion.IsWindows10OrLater && !Maximised) + { + // Somehow DWM doesn't calculate it corectly, there is a 1 pixel border around the capture + // Remove this border, currently it's fixed but TODO: Make it depend on the OS? + windowRect.Inflate(Conf.Win10BorderCrop); + _previousWindowRectangle = windowRect; + _lastWindowRectangleRetrieveTime = now; + return windowRect; } - - // Correction for maximized windows, only if it's not an app - if (!HasParent && !IsApp && Maximised) { - // Only if the border size can be retrieved - if (GetBorderSize(out var size)) - { - windowRect = new Rectangle(windowRect.X + size.Width, windowRect.Y + size.Height, windowRect.Width - (2 * size.Width), windowRect.Height - (2 * size.Height)); - } - } - _lastWindowRectangleRetrieveTime = now; - // Try to return something valid, by getting returning the previous size if the window doesn't have a Rectangle anymore - if (windowRect.IsEmpty) { - return _previousWindowRectangle; - } - _previousWindowRectangle = windowRect; - return windowRect; } + + if (windowRect.IsEmpty) { + if (!GetWindowRect(out windowRect)) + { + Win32Error error = Win32.GetLastErrorCode(); + Log.WarnFormat("Couldn't retrieve the windows rectangle: {0}", Win32.GetMessage(error)); + } + } + + // Correction for maximized windows, only if it's not an app + if (!HasParent && !IsApp && Maximised) { + // Only if the border size can be retrieved + if (GetBorderSize(out var size)) + { + windowRect = new Rectangle(windowRect.X + size.Width, windowRect.Y + size.Height, windowRect.Width - (2 * size.Width), windowRect.Height - (2 * size.Height)); + } + } + _lastWindowRectangleRetrieveTime = now; + // Try to return something valid, by getting returning the previous size if the window doesn't have a Rectangle anymore + if (windowRect.IsEmpty) { + return _previousWindowRectangle; + } + _previousWindowRectangle = windowRect; + return windowRect; } return _previousWindowRectangle; } @@ -752,12 +758,8 @@ namespace GreenshotPlugin.Core /// Get / Set the WindowStyle /// public WindowStyleFlags WindowStyle { - get { - return (WindowStyleFlags)User32.GetWindowLongWrapper(Handle, (int)WindowLongIndex.GWL_STYLE); - } - set { - User32.SetWindowLongWrapper(Handle, (int)WindowLongIndex.GWL_STYLE, new IntPtr((long)value)); - } + get => (WindowStyleFlags)User32.GetWindowLongWrapper(Handle, (int)WindowLongIndex.GWL_STYLE); + set => User32.SetWindowLongWrapper(Handle, (int)WindowLongIndex.GWL_STYLE, new IntPtr((long)value)); } /// @@ -778,12 +780,8 @@ namespace GreenshotPlugin.Core /// Get/Set the Extended WindowStyle /// public ExtendedWindowStyleFlags ExtendedWindowStyle { - get { - return (ExtendedWindowStyleFlags)User32.GetWindowLongWrapper(Handle, (int)WindowLongIndex.GWL_EXSTYLE); - } - set { - User32.SetWindowLongWrapper(Handle, (int)WindowLongIndex.GWL_EXSTYLE, new IntPtr((uint)value)); - } + get => (ExtendedWindowStyleFlags)User32.GetWindowLongWrapper(Handle, (int)WindowLongIndex.GWL_EXSTYLE); + set => User32.SetWindowLongWrapper(Handle, (int)WindowLongIndex.GWL_EXSTYLE, new IntPtr((uint)value)); } /// @@ -806,7 +804,7 @@ namespace GreenshotPlugin.Core /// /// Capture to fill /// Wanted WindowCaptureMode - /// True if auto modus is used + /// True if auto mode is used /// ICapture with the capture public ICapture CaptureDwmWindow(ICapture capture, WindowCaptureMode windowCaptureMode, bool autoMode) { IntPtr thumbnailHandle = IntPtr.Zero; @@ -838,6 +836,7 @@ namespace GreenshotPlugin.Core if (!Maximised) { // Assume using it's own location formLocation = windowRectangle.Location; + // TODO: Use Rectangle.Union! using Region workingArea = new Region(Screen.PrimaryScreen.Bounds); // Find the screen where the window is and check if it fits foreach (Screen screen in Screen.AllScreens) { @@ -1152,12 +1151,12 @@ namespace GreenshotPlugin.Core if (workaround) { const byte alt = 0xA4; - const int extendedkey = 0x1; + const int extendedKey = 0x1; const int keyup = 0x2; // Simulate an "ALT" key press. - User32.keybd_event(alt, 0x45, extendedkey | 0, 0); + User32.keybd_event(alt, 0x45, extendedKey | 0, 0); // Simulate an "ALT" key release. - User32.keybd_event(alt, 0x45, extendedkey | keyup, 0); + User32.keybd_event(alt, 0x45, extendedKey | keyup, 0); } // Show window in forground. User32.BringWindowToTop(handle); diff --git a/GreenshotPlugin/Core/WindowsEnumerator.cs b/GreenshotPlugin/Core/WindowsEnumerator.cs index da87e94d4..2df7fce54 100644 --- a/GreenshotPlugin/Core/WindowsEnumerator.cs +++ b/GreenshotPlugin/Core/WindowsEnumerator.cs @@ -59,7 +59,6 @@ namespace GreenshotPlugin.Core { /// Window Classname to copy, use null to copy all public WindowsEnumerator GetWindows(IntPtr hWndParent, string classname) { Items = new List(); - IList windows = new List(); User32.EnumChildWindows(hWndParent, WindowEnum, IntPtr.Zero); bool hasParent = !IntPtr.Zero.Equals(hWndParent); @@ -70,6 +69,7 @@ namespace GreenshotPlugin.Core { parentText = title.ToString(); } + List windows = new List(); foreach (var window in Items) { if (hasParent) { window.Text = parentText; @@ -104,7 +104,7 @@ namespace GreenshotPlugin.Core { /// /// Window handle to add /// True to continue enumeration, False to stop - protected bool OnWindowEnum(IntPtr hWnd) { + private bool OnWindowEnum(IntPtr hWnd) { if (!WindowDetails.IsIgnoreHandle(hWnd)) { Items.Add(new WindowDetails(hWnd)); } diff --git a/GreenshotPlugin/UnmanagedHelpers/DWM.cs b/GreenshotPlugin/UnmanagedHelpers/DWM.cs index 84bee66e4..0a801fe57 100644 --- a/GreenshotPlugin/UnmanagedHelpers/DWM.cs +++ b/GreenshotPlugin/UnmanagedHelpers/DWM.cs @@ -29,8 +29,8 @@ using Microsoft.Win32; namespace GreenshotPlugin.UnmanagedHelpers { - /// - /// Description of DWM. + /// + /// Desktop Window Manager helper code /// public static class DWM { public static readonly uint DWM_EC_DISABLECOMPOSITION = 0; @@ -75,12 +75,12 @@ namespace GreenshotPlugin.UnmanagedHelpers { /// bool public static bool IsWindowCloaked(IntPtr hWnd) { - if (WindowsVersion.IsWindows8OrLater) + if (!WindowsVersion.IsWindows8OrLater) { return false; } - DwmGetWindowAttribute(hWnd, DWMWINDOWATTRIBUTE.DWMWA_CLOAKED, out bool isCloaked, sizeof(bool)); + DwmGetWindowAttribute(hWnd, DWMWINDOWATTRIBUTE.DWMWA_CLOAKED, out bool isCloaked, Marshal.SizeOf(typeof(bool))); return isCloaked; } @@ -88,18 +88,23 @@ namespace GreenshotPlugin.UnmanagedHelpers { /// Helper method for an easy DWM check /// /// bool true if DWM is available AND active - public static bool IsDwmEnabled() { - // According to: http://technet.microsoft.com/en-us/subscriptions/aa969538%28v=vs.85%29.aspx - // And: http://msdn.microsoft.com/en-us/library/windows/desktop/aa969510%28v=vs.85%29.aspx - // DMW is always enabled on Windows 8! So return true and save a check! ;-) - if (WindowsVersion.IsWindows8OrLater) { - return true; + public static bool IsDwmEnabled { + get + { + // According to: http://technet.microsoft.com/en-us/subscriptions/aa969538%28v=vs.85%29.aspx + // And: http://msdn.microsoft.com/en-us/library/windows/desktop/aa969510%28v=vs.85%29.aspx + // DMW is always enabled on Windows 8! So return true and save a check! ;-) + if (WindowsVersion.IsWindows8OrLater) + { + return true; + } + if (WindowsVersion.IsWindowsVistaOrLater) + { + DwmIsCompositionEnabled(out var dwmEnabled); + return dwmEnabled; + } + return false; } - if (WindowsVersion.IsWindowsVistaOrLater) { - DwmIsCompositionEnabled(out var dwmEnabled); - return dwmEnabled; - } - return false; } public static Color ColorizationColor { diff --git a/GreenshotPlugin/UnmanagedHelpers/EnumWindowsProc.cs b/GreenshotPlugin/UnmanagedHelpers/EnumWindowsProc.cs index 9ab13906c..fdec7c001 100644 --- a/GreenshotPlugin/UnmanagedHelpers/EnumWindowsProc.cs +++ b/GreenshotPlugin/UnmanagedHelpers/EnumWindowsProc.cs @@ -1,3 +1,24 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + using System; namespace GreenshotPlugin.UnmanagedHelpers @@ -5,8 +26,8 @@ namespace GreenshotPlugin.UnmanagedHelpers /// /// Used with EnumWindows or EnumChildWindows /// - /// - /// - /// - public delegate int EnumWindowsProc(IntPtr hwnd, int lParam); + /// IntPtr + /// int + /// int + public delegate int EnumWindowsProc(IntPtr hWnd, int lParam); } \ No newline at end of file