BUG-2553, BUG-2535: This should fix the issues we are having with selection / presenting the wrong windows. (Previous commit had a bug)

This commit is contained in:
Robin 2020-03-03 09:10:33 +01:00
commit 0202c2fa8b
8 changed files with 129 additions and 103 deletions

View file

@ -97,7 +97,7 @@ namespace Greenshot.Forms {
public WindowDetails SelectedCaptureWindow => _selectedCaptureWindow; public WindowDetails SelectedCaptureWindow => _selectedCaptureWindow;
/// <summary> /// <summary>
/// This should prevent childs to draw backgrounds /// This should prevent children to draw backgrounds
/// </summary> /// </summary>
protected override CreateParams CreateParams { protected override CreateParams CreateParams {
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
@ -119,7 +119,7 @@ namespace Greenshot.Forms {
} }
private void ClosingHandler(object sender, EventArgs e) { private void ClosingHandler(object sender, EventArgs e) {
Log.Debug("Closing captureform"); Log.Debug("Closing capture form");
WindowDetails.UnregisterIgnoreHandle(Handle); WindowDetails.UnregisterIgnoreHandle(Handle);
} }

View file

@ -927,7 +927,7 @@ namespace Greenshot {
public void AddCaptureWindowMenuItems(ToolStripMenuItem menuItem, EventHandler eventHandler) { public void AddCaptureWindowMenuItems(ToolStripMenuItem menuItem, EventHandler eventHandler) {
menuItem.DropDownItems.Clear(); menuItem.DropDownItems.Clear();
// check if thumbnailPreview is enabled and DWM is enabled // 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()) { foreach(WindowDetails window in WindowDetails.GetTopLevelWindows()) {

View file

@ -193,7 +193,7 @@ namespace Greenshot {
private void SetWindowCaptureMode(WindowCaptureMode selectedWindowCaptureMode) { private void SetWindowCaptureMode(WindowCaptureMode selectedWindowCaptureMode) {
WindowCaptureMode[] availableModes; WindowCaptureMode[] availableModes;
if (!DWM.IsDwmEnabled()) { if (!DWM.IsDwmEnabled) {
// Remove DWM from configuration, as DWM is disabled! // Remove DWM from configuration, as DWM is disabled!
if (coreConfiguration.WindowCaptureMode == WindowCaptureMode.Aero || coreConfiguration.WindowCaptureMode == WindowCaptureMode.AeroTransparent) { if (coreConfiguration.WindowCaptureMode == WindowCaptureMode.Aero || coreConfiguration.WindowCaptureMode == WindowCaptureMode.AeroTransparent) {
coreConfiguration.WindowCaptureMode = WindowCaptureMode.GDI; coreConfiguration.WindowCaptureMode = WindowCaptureMode.GDI;

View file

@ -813,7 +813,7 @@ namespace Greenshot.Helpers {
Rectangle windowRectangle = windowToCapture.WindowRectangle; Rectangle windowRectangle = windowToCapture.WindowRectangle;
// When Vista & DWM (Aero) enabled // 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 // get process name to be able to exclude certain processes from certain capture modes
using (Process process = windowToCapture.Process) { using (Process process = windowToCapture.Process) {
bool isAutoMode = windowCaptureMode == WindowCaptureMode.Auto; bool isAutoMode = windowCaptureMode == WindowCaptureMode.Auto;
@ -1010,26 +1010,27 @@ namespace Greenshot.Helpers {
} finally { } finally {
captureForm.Hide(); 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) { if (result != DialogResult.OK) return;
// Take the captureRect, this already is specified as bitmap coordinates
_capture.Crop(_captureRect);
// save for re-capturing later and show recapture context menu option _selectedCaptureWindow = captureForm.SelectedCaptureWindow;
// Important here is that the location needs to be offsetted back to screen coordinates! _captureRect = captureForm.CaptureRectangle;
Rectangle tmpRectangle = _captureRect; // Get title
tmpRectangle.Offset(_capture.ScreenBounds.Location.X, _capture.ScreenBounds.Location.Y); if (_selectedCaptureWindow != null) {
CoreConfig.LastCapturedRegion = tmpRectangle; _capture.CaptureDetails.Title = _selectedCaptureWindow.Text;
}
HandleCapture();
} }
}
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();
}
} }
} }

View file

@ -553,6 +553,11 @@ namespace GreenshotPlugin.Core
/// </summary> /// </summary>
public bool Visible { public bool Visible {
get { get {
// Tip from Raymond Chen
if (DWM.IsWindowCloaked(Handle))
{
return false;
}
if (IsApp) { if (IsApp) {
Rectangle windowRectangle = WindowRectangle; Rectangle windowRectangle = WindowRectangle;
foreach (Screen screen in Screen.AllScreens) { foreach (Screen screen in Screen.AllScreens) {
@ -584,8 +589,7 @@ namespace GreenshotPlugin.Core
if (IsAppLauncher) { if (IsAppLauncher) {
return IsAppLauncherVisible; return IsAppLauncherVisible;
} }
// Tip from Raymond Chen return User32.IsWindowVisible(Handle);
return User32.IsWindowVisible(Handle) && !DWM.IsWindowCloaked(Handle);
} }
} }
@ -634,55 +638,57 @@ namespace GreenshotPlugin.Core
// Try to return a cached value // Try to return a cached value
long now = DateTime.Now.Ticks; long now = DateTime.Now.Ticks;
if (_previousWindowRectangle.IsEmpty || !_frozen) { if (_previousWindowRectangle.IsEmpty || !_frozen) {
if (_previousWindowRectangle.IsEmpty || now - _lastWindowRectangleRetrieveTime > CacheTime) { if (!_previousWindowRectangle.IsEmpty && now - _lastWindowRectangleRetrieveTime <= CacheTime)
Rectangle windowRect = Rectangle.Empty; {
if (DWM.IsDwmEnabled()) return _previousWindowRectangle;
}
Rectangle windowRect = Rectangle.Empty;
if (DWM.IsDwmEnabled)
{
bool gotFrameBounds = GetExtendedFrameBounds(out windowRect);
if (IsApp)
{ {
bool gotFrameBounds = GetExtendedFrameBounds(out windowRect); // Pre-Cache for maximized call, this is only on Windows 8 apps (full screen)
if (IsApp) 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; _previousWindowRectangle = windowRect;
_lastWindowRectangleRetrieveTime = now; _lastWindowRectangleRetrieveTime = now;
return windowRect;
} }
} }
if (gotFrameBounds && WindowsVersion.IsWindows10OrLater && !Maximised)
if (windowRect.IsEmpty) { {
if (!GetWindowRect(out windowRect)) // 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?
Win32Error error = Win32.GetLastErrorCode(); windowRect.Inflate(Conf.Win10BorderCrop);
Log.WarnFormat("Couldn't retrieve the windows rectangle: {0}", Win32.GetMessage(error)); _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; return _previousWindowRectangle;
} }
@ -752,12 +758,8 @@ namespace GreenshotPlugin.Core
/// Get / Set the WindowStyle /// Get / Set the WindowStyle
/// </summary> /// </summary>
public WindowStyleFlags WindowStyle { public WindowStyleFlags WindowStyle {
get { get => (WindowStyleFlags)User32.GetWindowLongWrapper(Handle, (int)WindowLongIndex.GWL_STYLE);
return (WindowStyleFlags)User32.GetWindowLongWrapper(Handle, (int)WindowLongIndex.GWL_STYLE); set => User32.SetWindowLongWrapper(Handle, (int)WindowLongIndex.GWL_STYLE, new IntPtr((long)value));
}
set {
User32.SetWindowLongWrapper(Handle, (int)WindowLongIndex.GWL_STYLE, new IntPtr((long)value));
}
} }
/// <summary> /// <summary>
@ -778,12 +780,8 @@ namespace GreenshotPlugin.Core
/// Get/Set the Extended WindowStyle /// Get/Set the Extended WindowStyle
/// </summary> /// </summary>
public ExtendedWindowStyleFlags ExtendedWindowStyle { public ExtendedWindowStyleFlags ExtendedWindowStyle {
get { get => (ExtendedWindowStyleFlags)User32.GetWindowLongWrapper(Handle, (int)WindowLongIndex.GWL_EXSTYLE);
return (ExtendedWindowStyleFlags)User32.GetWindowLongWrapper(Handle, (int)WindowLongIndex.GWL_EXSTYLE); set => User32.SetWindowLongWrapper(Handle, (int)WindowLongIndex.GWL_EXSTYLE, new IntPtr((uint)value));
}
set {
User32.SetWindowLongWrapper(Handle, (int)WindowLongIndex.GWL_EXSTYLE, new IntPtr((uint)value));
}
} }
/// <summary> /// <summary>
@ -806,7 +804,7 @@ namespace GreenshotPlugin.Core
/// </summary> /// </summary>
/// <param name="capture">Capture to fill</param> /// <param name="capture">Capture to fill</param>
/// <param name="windowCaptureMode">Wanted WindowCaptureMode</param> /// <param name="windowCaptureMode">Wanted WindowCaptureMode</param>
/// <param name="autoMode">True if auto modus is used</param> /// <param name="autoMode">True if auto mode is used</param>
/// <returns>ICapture with the capture</returns> /// <returns>ICapture with the capture</returns>
public ICapture CaptureDwmWindow(ICapture capture, WindowCaptureMode windowCaptureMode, bool autoMode) { public ICapture CaptureDwmWindow(ICapture capture, WindowCaptureMode windowCaptureMode, bool autoMode) {
IntPtr thumbnailHandle = IntPtr.Zero; IntPtr thumbnailHandle = IntPtr.Zero;
@ -838,6 +836,7 @@ namespace GreenshotPlugin.Core
if (!Maximised) { if (!Maximised) {
// Assume using it's own location // Assume using it's own location
formLocation = windowRectangle.Location; formLocation = windowRectangle.Location;
// TODO: Use Rectangle.Union!
using Region workingArea = new Region(Screen.PrimaryScreen.Bounds); using Region workingArea = new Region(Screen.PrimaryScreen.Bounds);
// Find the screen where the window is and check if it fits // Find the screen where the window is and check if it fits
foreach (Screen screen in Screen.AllScreens) { foreach (Screen screen in Screen.AllScreens) {
@ -1152,12 +1151,12 @@ namespace GreenshotPlugin.Core
if (workaround) if (workaround)
{ {
const byte alt = 0xA4; const byte alt = 0xA4;
const int extendedkey = 0x1; const int extendedKey = 0x1;
const int keyup = 0x2; const int keyup = 0x2;
// Simulate an "ALT" key press. // 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. // 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. // Show window in forground.
User32.BringWindowToTop(handle); User32.BringWindowToTop(handle);

View file

@ -59,7 +59,6 @@ namespace GreenshotPlugin.Core {
/// <param name="classname">Window Classname to copy, use null to copy all</param> /// <param name="classname">Window Classname to copy, use null to copy all</param>
public WindowsEnumerator GetWindows(IntPtr hWndParent, string classname) { public WindowsEnumerator GetWindows(IntPtr hWndParent, string classname) {
Items = new List<WindowDetails>(); Items = new List<WindowDetails>();
IList<WindowDetails> windows = new List<WindowDetails>();
User32.EnumChildWindows(hWndParent, WindowEnum, IntPtr.Zero); User32.EnumChildWindows(hWndParent, WindowEnum, IntPtr.Zero);
bool hasParent = !IntPtr.Zero.Equals(hWndParent); bool hasParent = !IntPtr.Zero.Equals(hWndParent);
@ -70,6 +69,7 @@ namespace GreenshotPlugin.Core {
parentText = title.ToString(); parentText = title.ToString();
} }
List<WindowDetails> windows = new List<WindowDetails>();
foreach (var window in Items) { foreach (var window in Items) {
if (hasParent) { if (hasParent) {
window.Text = parentText; window.Text = parentText;
@ -104,7 +104,7 @@ namespace GreenshotPlugin.Core {
/// </summary> /// </summary>
/// <param name="hWnd">Window handle to add</param> /// <param name="hWnd">Window handle to add</param>
/// <returns>True to continue enumeration, False to stop</returns> /// <returns>True to continue enumeration, False to stop</returns>
protected bool OnWindowEnum(IntPtr hWnd) { private bool OnWindowEnum(IntPtr hWnd) {
if (!WindowDetails.IsIgnoreHandle(hWnd)) { if (!WindowDetails.IsIgnoreHandle(hWnd)) {
Items.Add(new WindowDetails(hWnd)); Items.Add(new WindowDetails(hWnd));
} }

View file

@ -29,8 +29,8 @@ using Microsoft.Win32;
namespace GreenshotPlugin.UnmanagedHelpers { namespace GreenshotPlugin.UnmanagedHelpers {
/// <summary> /// <summary>
/// Description of DWM. /// Desktop Window Manager helper code
/// </summary> /// </summary>
public static class DWM { public static class DWM {
public static readonly uint DWM_EC_DISABLECOMPOSITION = 0; public static readonly uint DWM_EC_DISABLECOMPOSITION = 0;
@ -75,12 +75,12 @@ namespace GreenshotPlugin.UnmanagedHelpers {
/// <returns>bool</returns> /// <returns>bool</returns>
public static bool IsWindowCloaked(IntPtr hWnd) public static bool IsWindowCloaked(IntPtr hWnd)
{ {
if (WindowsVersion.IsWindows8OrLater) if (!WindowsVersion.IsWindows8OrLater)
{ {
return false; 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; return isCloaked;
} }
@ -88,18 +88,23 @@ namespace GreenshotPlugin.UnmanagedHelpers {
/// Helper method for an easy DWM check /// Helper method for an easy DWM check
/// </summary> /// </summary>
/// <returns>bool true if DWM is available AND active</returns> /// <returns>bool true if DWM is available AND active</returns>
public static bool IsDwmEnabled() { public static bool IsDwmEnabled {
// According to: http://technet.microsoft.com/en-us/subscriptions/aa969538%28v=vs.85%29.aspx get
// 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! ;-) // According to: http://technet.microsoft.com/en-us/subscriptions/aa969538%28v=vs.85%29.aspx
if (WindowsVersion.IsWindows8OrLater) { // And: http://msdn.microsoft.com/en-us/library/windows/desktop/aa969510%28v=vs.85%29.aspx
return true; // 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 { public static Color ColorizationColor {

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System; using System;
namespace GreenshotPlugin.UnmanagedHelpers namespace GreenshotPlugin.UnmanagedHelpers
@ -5,8 +26,8 @@ namespace GreenshotPlugin.UnmanagedHelpers
/// <summary> /// <summary>
/// Used with EnumWindows or EnumChildWindows /// Used with EnumWindows or EnumChildWindows
/// </summary> /// </summary>
/// <param name="hwnd"></param> /// <param name="hWnd">IntPtr</param>
/// <param name="lParam"></param> /// <param name="lParam">int</param>
/// <returns></returns> /// <returns>int</returns>
public delegate int EnumWindowsProc(IntPtr hwnd, int lParam); public delegate int EnumWindowsProc(IntPtr hWnd, int lParam);
} }