From 9ebd4491af80fdd9d632850b753fee15060894c0 Mon Sep 17 00:00:00 2001 From: Robin Date: Tue, 3 Mar 2020 08:14:16 +0100 Subject: [PATCH] This contains a fix for SUPPORT-288 which was suggested by Raymond Chen here: https://devblogs.microsoft.com/oldnewthing/20200302-00/?p=103507 --- Greenshot/Forms/SettingsForm.cs | 1 + GreenshotPlugin/Core/WindowDetails.cs | 1668 ++++++++++++++++ GreenshotPlugin/Core/WindowsEnumerator.cs | 114 ++ GreenshotPlugin/Core/WindowsHelper.cs | 1779 ----------------- .../UnmanagedHelpers/{Enums => }/DWM.cs | 98 +- .../Enums/DWMWINDOWATTRIBUTE.cs | 26 +- .../UnmanagedHelpers/Enums/DWM_BB.cs | 23 +- .../UnmanagedHelpers/Enums/DWM_BLURBEHIND.cs | 34 + .../Enums/DWM_THUMBNAIL_PROPERTIES.cs | 85 + .../UnmanagedHelpers/Enums/SYSCOLOR.cs | 23 +- GreenshotPlugin/UnmanagedHelpers/User32.cs | 18 +- 11 files changed, 2006 insertions(+), 1863 deletions(-) create mode 100644 GreenshotPlugin/Core/WindowDetails.cs create mode 100644 GreenshotPlugin/Core/WindowsEnumerator.cs delete mode 100644 GreenshotPlugin/Core/WindowsHelper.cs rename GreenshotPlugin/UnmanagedHelpers/{Enums => }/DWM.cs (55%) create mode 100644 GreenshotPlugin/UnmanagedHelpers/Enums/DWM_BLURBEHIND.cs create mode 100644 GreenshotPlugin/UnmanagedHelpers/Enums/DWM_THUMBNAIL_PROPERTIES.cs diff --git a/Greenshot/Forms/SettingsForm.cs b/Greenshot/Forms/SettingsForm.cs index b55dcc9bd..d9098e7b4 100644 --- a/Greenshot/Forms/SettingsForm.cs +++ b/Greenshot/Forms/SettingsForm.cs @@ -37,6 +37,7 @@ using System.Text.RegularExpressions; using GreenshotPlugin.IniFile; using GreenshotPlugin.Interfaces; using GreenshotPlugin.Interfaces.Plugin; +using GreenshotPlugin.UnmanagedHelpers; using GreenshotPlugin.UnmanagedHelpers.Enums; using log4net; diff --git a/GreenshotPlugin/Core/WindowDetails.cs b/GreenshotPlugin/Core/WindowDetails.cs new file mode 100644 index 000000000..8f3579c4b --- /dev/null +++ b/GreenshotPlugin/Core/WindowDetails.cs @@ -0,0 +1,1668 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Imaging; +using System.Runtime.InteropServices; +using System.Text; +using System.Text.RegularExpressions; +using System.Windows.Forms; +using GreenshotPlugin.IniFile; +using GreenshotPlugin.Interfaces; +using GreenshotPlugin.Interop; +using GreenshotPlugin.UnmanagedHelpers; +using GreenshotPlugin.UnmanagedHelpers.Enums; +using GreenshotPlugin.UnmanagedHelpers.Structs; +using log4net; + +namespace GreenshotPlugin.Core +{ + /// + /// Code for handling with "windows" + /// Main code is taken from vbAccelerator, location: + /// http://www.vbaccelerator.com/home/NET/Code/Libraries/Windows/Enumerating_Windows/article.asp + /// but a LOT of changes/enhancements were made to adapt it for Greenshot. + /// + /// 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"; + private static readonly IList IgnoreClasses = new List(new[] { "Progman", "Button", "Dwm" }); //"MS-SDIa" + + private static readonly ILog Log = LogManager.GetLogger(typeof(WindowDetails)); + private static readonly CoreConfiguration Conf = IniConfig.GetIniSection(); + private static readonly IList IgnoreHandles = new List(); + private static readonly IList ExcludeProcessesFromFreeze = new List(); + private static readonly IAppVisibility AppVisibility; + + static WindowDetails() { + try + { + // Only try to instantiate when Windows 8 or later. + if (WindowsVersion.IsWindows8OrLater) + { + AppVisibility = COMWrapper.CreateInstance(); + } + } + catch (Exception ex) + { + Log.WarnFormat("Couldn't create instance of IAppVisibility: {0}", ex.Message); + } + } + + public static void AddProcessToExcludeFromFreeze(string processName) { + if (!ExcludeProcessesFromFreeze.Contains(processName)) { + ExcludeProcessesFromFreeze.Add(processName); + } + } + + internal static bool IsIgnoreHandle(IntPtr handle) { + return IgnoreHandles.Contains(handle); + } + + private IList _childWindows; + private IntPtr _parentHandle = IntPtr.Zero; + private WindowDetails _parent; + private bool _frozen; + + /// + /// 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); + + /// + /// This checks if the window is a Windows 10 App + /// For Windows 10 apps are hosted inside "ApplicationFrameWindow" + /// + public bool IsWin10App => FramedAppClass.Equals(ClassName); + + /// + /// Check if the window is the metro gutter (sizeable separator) + /// + public bool IsGutter => MetroGutterClass.Equals(ClassName); + + /// + /// Test if this window is for the App-Launcher + /// + public bool IsAppLauncher => MetroApplauncherClass.Equals(ClassName); + + /// + /// Check if this window is the window of a metro app + /// + public bool IsMetroApp => IsAppLauncher || IsApp; + + /// + /// To allow items to be compared, the hash code + /// is set to the Window handle, so two EnumWindowsItem + /// objects for the same Window will be equal. + /// + /// The Window Handle for this window + public override int GetHashCode() { + return Handle.ToInt32(); + } + + public override bool Equals(object right) { + return Equals(right as WindowDetails); + } + + /// + /// Compare two windows details + /// + /// + /// + public bool Equals(WindowDetails other) { + if (other is null) { + return false; + } + + if (ReferenceEquals(this, other)) { + return true; + } + + if (GetType() != other.GetType()){ + return false; + } + return other.Handle == Handle; + } + + /// + /// Check if the window has children + /// + public bool HasChildren => (_childWindows != null) && (_childWindows.Count > 0); + + /// + /// Freeze information updates + /// + public void FreezeDetails() { + _frozen = true; + } + + /// + /// Make the information update again. + /// + public void UnfreezeDetails() { + _frozen = false; + } + + /// + /// Get the file path to the exe for the process which owns this window + /// + public string ProcessPath { + get { + if (Handle == IntPtr.Zero) { + // not a valid window handle + return string.Empty; + } + // Get the process id + User32.GetWindowThreadProcessId(Handle, out var processId); + return Kernel32.GetProcessPath(processId); + } + } + + + /// + /// Get the icon belonging to the process + /// + public Image DisplayIcon { + get { + try + { + using var appIcon = GetAppIcon(Handle); + if (appIcon != null) { + return appIcon.ToBitmap(); + } + } catch (Exception ex) { + Log.WarnFormat("Couldn't get icon for window {0} due to: {1}", Text, ex.Message); + Log.Warn(ex); + } + if (IsMetroApp) { + // No method yet to get the metro icon + return null; + } + try { + return PluginUtils.GetCachedExeIcon(ProcessPath, 0); + } catch (Exception ex) { + Log.WarnFormat("Couldn't get icon for window {0} due to: {1}", Text, ex.Message); + Log.Warn(ex); + } + return null; + } + } + + /// + /// Get the icon for a hWnd + /// + /// + /// + private static Icon GetAppIcon(IntPtr hwnd) { + IntPtr iconSmall = IntPtr.Zero; + IntPtr iconBig = new IntPtr(1); + IntPtr iconSmall2 = new IntPtr(2); + + IntPtr iconHandle; + if (Conf.UseLargeIcons) { + iconHandle = User32.SendMessage(hwnd, (int)WindowsMessages.WM_GETICON, iconBig, IntPtr.Zero); + if (iconHandle == IntPtr.Zero) { + iconHandle = User32.GetClassLongWrapper(hwnd, (int)ClassLongIndex.GCL_HICON); + } + } else { + iconHandle = User32.SendMessage(hwnd, (int)WindowsMessages.WM_GETICON, iconSmall2, IntPtr.Zero); + } + if (iconHandle == IntPtr.Zero) { + iconHandle = User32.SendMessage(hwnd, (int)WindowsMessages.WM_GETICON, iconSmall, IntPtr.Zero); + } + if (iconHandle == IntPtr.Zero) { + iconHandle = User32.GetClassLongWrapper(hwnd, (int)ClassLongIndex.GCL_HICONSM); + } + if (iconHandle == IntPtr.Zero) { + iconHandle = User32.SendMessage(hwnd, (int)WindowsMessages.WM_GETICON, iconBig, IntPtr.Zero); + } + if (iconHandle == IntPtr.Zero) { + iconHandle = User32.GetClassLongWrapper(hwnd, (int)ClassLongIndex.GCL_HICON); + } + + if (iconHandle == IntPtr.Zero) { + return null; + } + + Icon icon = Icon.FromHandle(iconHandle); + + return icon; + } + + /// + /// Use this to make remove internal windows, like the mainform and the captureforms, invisible + /// + /// + public static void RegisterIgnoreHandle(IntPtr ignoreHandle) { + IgnoreHandles.Add(ignoreHandle); + } + + /// + /// Use this to remove the with RegisterIgnoreHandle registered handle + /// + /// + public static void UnregisterIgnoreHandle(IntPtr ignoreHandle) { + IgnoreHandles.Remove(ignoreHandle); + } + + public IList Children { + get { + if (_childWindows == null) { + GetChildren(); + } + return _childWindows; + } + } + + /// + /// Retrieve all windows with a certain title or classname + /// + /// IEnumerable + /// The regexp to look for in the title + /// The regexp to look for in the classname + /// IEnumerable WindowDetails with all the found windows + private static IEnumerable FindWindow(IEnumerable windows, string titlePattern, string classnamePattern) { + Regex titleRegexp = null; + Regex classnameRegexp = null; + + if (titlePattern != null && titlePattern.Trim().Length > 0) { + titleRegexp = new Regex(titlePattern); + } + if (classnamePattern != null && classnamePattern.Trim().Length > 0) { + classnameRegexp = new Regex(classnamePattern); + } + + foreach(WindowDetails window in windows) { + if (titleRegexp != null && titleRegexp.IsMatch(window.Text)) { + yield return window; + } else if (classnameRegexp != null && classnameRegexp.IsMatch(window.ClassName)) { + yield return window; + } + } + } + + /// + /// Retrieve the child with matching classname + /// + public WindowDetails GetChild(string childClassname) { + foreach(var child in Children) { + if (childClassname.Equals(child.ClassName)) { + return child; + } + } + return null; + } + + /// + /// Retrieve the children with matching classname + /// + public IEnumerable GetChilden(string childClassname) { + foreach (var child in Children) { + if (childClassname.Equals(child.ClassName)) { + yield return child; + } + } + } + + public IntPtr ParentHandle { + get { + if (_parentHandle == IntPtr.Zero) { + _parentHandle = User32.GetParent(Handle); + _parent = null; + } + return _parentHandle; + } + set { + if (_parentHandle != value) { + _parentHandle = value; + _parent = null; + } + } + } + /// + /// Get the parent of the current window + /// + /// WindowDetails of the parent, or null if none + public WindowDetails GetParent() { + if (_parent == null) { + if (_parentHandle == IntPtr.Zero) { + _parentHandle = User32.GetParent(Handle); + } + if (_parentHandle != IntPtr.Zero) { + _parent = new WindowDetails(_parentHandle); + } + } + return _parent; + } + + /// + /// Retrieve all the children, this only stores the children internally. + /// One should normally use the getter "Children" + /// + public IList GetChildren() { + if (_childWindows == null) { + return GetChildren(0); + } + return _childWindows; + } + + /// + /// Retrieve all the children, this only stores the children internally, use the "Children" property for the value + /// + /// Specify how many levels we go in + public IList GetChildren(int levelsToGo) { + if (_childWindows != null) + { + return _childWindows; + } + _childWindows = new WindowsEnumerator().GetWindows(Handle, null).Items; + foreach(var childWindow in _childWindows) { + if (levelsToGo > 0) { + childWindow.GetChildren(levelsToGo-1); + } + } + return _childWindows; + } + + /// + /// Retrieve children with a certain title or classname + /// + /// The regexp to look for in the title + /// The regexp to look for in the classname + /// List WindowDetails with all the found windows, or an empty list + public IEnumerable FindChildren(string titlePattern, string classnamePattern) { + return FindWindow(Children, titlePattern, classnamePattern); + } + + /// + /// Recurse-ing helper method for the FindPath + /// + /// List string with classNames + /// The index in the list to look for + /// WindowDetails if a match was found + private WindowDetails FindPath(IList classNames, int index) { + if (index == classNames.Count - 1) { + foreach (var foundWindow in FindChildren(null, classNames[index])) + { + return foundWindow; + } + } else { + foreach(var foundWindow in FindChildren(null, classNames[index])) + { + var resultWindow = foundWindow.FindPath(classNames, index+1); + if (resultWindow != null) + { + return resultWindow; + } + } + } + return null; + } + + /// + /// This method will find the child window according to a path of classNames. + /// Usually used for finding a certain "content" window like for the IE Browser + /// + /// List of string with classname "path" + /// true allows the search to skip a classname of the path + /// WindowDetails if found + public WindowDetails FindPath(IList classNames, bool allowSkip) { + int index = 0; + var resultWindow = FindPath(classNames, index++); + if (resultWindow == null && allowSkip) { + while(resultWindow == null && index < classNames.Count) { + resultWindow = FindPath(classNames, index); + } + } + return resultWindow; + } + + /// + /// Deep scan for a certain classname pattern + /// + /// Window to scan into + /// Classname regexp pattern + /// The first WindowDetails found + public static WindowDetails DeepScan(WindowDetails windowDetails, Regex classnamePattern) { + if (classnamePattern.IsMatch(windowDetails.ClassName)) { + return windowDetails; + } + // First loop through this level + foreach(var child in windowDetails.Children) { + if (classnamePattern.IsMatch(child.ClassName)) { + return child; + } + } + // Go into all children + foreach(var child in windowDetails.Children) { + var deepWindow = DeepScan(child, classnamePattern); + if (deepWindow != null) { + return deepWindow; + } + } + return null; + } + + /// + /// GetWindow + /// + /// The GetWindowCommand to use + /// null if nothing found, otherwise the WindowDetails instance of the "child" + public WindowDetails GetWindow(GetWindowCommand gwCommand) { + var tmphWnd = User32.GetWindow(Handle, gwCommand); + if (IntPtr.Zero == tmphWnd) { + return null; + } + var windowDetails = new WindowDetails(tmphWnd) + { + _parent = this + }; + return windowDetails; + } + + /// + /// Gets the window's handle + /// + public IntPtr Handle { get; } + + private string _text; + /// + /// Gets the window's title (caption) + /// + public string Text { + set => _text = value; + get { + if (_text == null) { + var title = new StringBuilder(260, 260); + User32.GetWindowText(Handle, title, title.Capacity); + _text = title.ToString(); + } + return _text; + } + } + + private string _className; + /// + /// Gets the window's class name. + /// + public string ClassName => _className ??= GetClassName(Handle); + + /// + /// Gets/Sets whether the window is iconic (minimized) or not. + /// + public bool Iconic { + get { + if (IsMetroApp) { + return !Visible; + } + return User32.IsIconic(Handle) || Location.X <= -32000; + } + set { + if (value) { + User32.SendMessage(Handle, (int)WindowsMessages.WM_SYSCOMMAND, (IntPtr)User32.SC_MINIMIZE, IntPtr.Zero); + } else { + User32.SendMessage(Handle, (int)WindowsMessages.WM_SYSCOMMAND, (IntPtr)User32.SC_RESTORE, IntPtr.Zero); + } + } + } + + /// + /// Gets/Sets whether the window is maximised or not. + /// + public bool Maximised { + get { + if (IsApp) + { + if (Visible) { + Rectangle windowRectangle = WindowRectangle; + foreach (var screen in Screen.AllScreens) { + if (screen.Bounds.Contains(windowRectangle)) { + if (windowRectangle.Equals(screen.Bounds)) { + return true; + } + } + } + } + return false; + } + return User32.IsZoomed(Handle); + } + set { + if (value) { + User32.SendMessage(Handle, (int)WindowsMessages.WM_SYSCOMMAND, (IntPtr)User32.SC_MAXIMIZE, IntPtr.Zero); + } else { + User32.SendMessage(Handle, (int)WindowsMessages.WM_SYSCOMMAND, (IntPtr)User32.SC_MINIMIZE, IntPtr.Zero); + } + } + } + + /// + /// This doesn't work as good as is should, but does move the App out of the way... + /// + public void HideApp() { + User32.ShowWindow(Handle, ShowWindowCommand.Hide); + } + + /// + /// Gets whether the window is visible. + /// + public bool Visible { + get { + if (IsApp) { + Rectangle windowRectangle = WindowRectangle; + foreach (Screen screen in Screen.AllScreens) { + if (screen.Bounds.Contains(windowRectangle)) { + if (windowRectangle.Equals(screen.Bounds)) { + // Fullscreen, it's "visible" when AppVisibilityOnMonitor says yes + // Although it might be the other App, this is not "very" important + RECT rect = new RECT(screen.Bounds); + IntPtr monitor = User32.MonitorFromRect(ref rect, User32.MONITOR_DEFAULTTONULL); + if (monitor != IntPtr.Zero) { + MONITOR_APP_VISIBILITY? monitorAppVisibility = AppVisibility?.GetAppVisibilityOnMonitor(monitor); + //LOG.DebugFormat("App {0} visible: {1} on {2}", Text, monitorAppVisibility, screen.Bounds); + if (monitorAppVisibility == MONITOR_APP_VISIBILITY.MAV_APP_VISIBLE) { + return true; + } + } + } else { + // Is only partly on the screen, when this happens the app is always visible! + return true; + } + } + } + return false; + } + if (IsGutter) { + // gutter is only made available when it's visible + return true; + } + if (IsAppLauncher) { + return IsAppLauncherVisible; + } + // Tip from Raymond Chen + return User32.IsWindowVisible(Handle) && !DWM.IsWindowCloaked(Handle); + } + } + + public bool HasParent { + get { + GetParent(); + return _parentHandle != IntPtr.Zero; + } + } + + public int ProcessId { + get { + User32.GetWindowThreadProcessId(Handle, out var processId); + return processId; + } + } + + public Process Process { + get { + try { + User32.GetWindowThreadProcessId(Handle, out var processId); + return Process.GetProcessById(processId); + } catch (Exception ex) { + Log.Warn(ex); + } + return null; + } + } + + /// + /// Make sure the next call of a cached value is guaranteed the real value + /// + public void Reset() { + _previousWindowRectangle = Rectangle.Empty; + } + + private Rectangle _previousWindowRectangle = Rectangle.Empty; + private long _lastWindowRectangleRetrieveTime; + private const long CacheTime = TimeSpan.TicksPerSecond * 2; + + /// + /// Gets the bounding rectangle of the window + /// + public Rectangle WindowRectangle { + get { + // 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()) + { + bool gotFrameBounds = GetExtendedFrameBounds(out windowRect); + if (IsApp) + { + // 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)); + } + } + + // 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; + } + } + + /// + /// Gets the location of the window relative to the screen. + /// + public Point Location { + get { + Rectangle tmpRectangle = WindowRectangle; + return new Point(tmpRectangle.Left, tmpRectangle.Top); + } + } + + /// + /// Gets the size of the window. + /// + public Size Size { + get { + Rectangle tmpRectangle = WindowRectangle; + return new Size(tmpRectangle.Right - tmpRectangle.Left, tmpRectangle.Bottom - tmpRectangle.Top); + } + } + + /// + /// Get the client rectangle, this is the part of the window inside the borders (drawable area) + /// + public Rectangle ClientRectangle { + get { + if (!GetClientRect(out var clientRect)) + { + Win32Error error = Win32.GetLastErrorCode(); + Log.WarnFormat("Couldn't retrieve the client rectangle for {0}, error: {1}", Text, Win32.GetMessage(error)); + } + return clientRect; + } + } + + /// + /// Check if the supplied point lies in the window + /// + /// Point with the coordinates to check + /// true if the point lies within + public bool Contains(Point p) { + return WindowRectangle.Contains(p); + } + + /// + /// Restores and Brings the window to the front, + /// assuming it is a visible application window. + /// + public void Restore() { + if (Iconic) { + User32.SendMessage(Handle, (int)WindowsMessages.WM_SYSCOMMAND, (IntPtr)User32.SC_RESTORE, IntPtr.Zero); + } + User32.BringWindowToTop(Handle); + User32.SetForegroundWindow(Handle); + // Make sure windows has time to perform the action + // TODO: this is BAD practice! + while(Iconic) { + Application.DoEvents(); + } + } + + /// + /// 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/Set the WindowPlacement + /// + public WindowPlacement WindowPlacement { + get { + var placement = WindowPlacement.Default; + User32.GetWindowPlacement(Handle, ref placement); + return placement; + } + set { + User32.SetWindowPlacement(Handle, ref value); + } + } + + /// + /// 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)); + } + } + + /// + /// Capture Window with GDI+ + /// + /// The capture to fill + /// ICapture + public ICapture CaptureGdiWindow(ICapture capture) { + Image capturedImage = PrintWindow(); + if (capturedImage != null) { + capture.Image = capturedImage; + capture.Location = Location; + return capture; + } + return null; + } + + /// + /// Capture DWM Window + /// + /// Capture to fill + /// Wanted WindowCaptureMode + /// True if auto modus is used + /// ICapture with the capture + public ICapture CaptureDwmWindow(ICapture capture, WindowCaptureMode windowCaptureMode, bool autoMode) { + IntPtr thumbnailHandle = IntPtr.Zero; + Form tempForm = null; + bool tempFormShown = false; + try { + tempForm = new Form + { + ShowInTaskbar = false, + FormBorderStyle = FormBorderStyle.None, + TopMost = true + }; + + // Register the Thumbnail + DWM.DwmRegisterThumbnail(tempForm.Handle, Handle, out thumbnailHandle); + + // Get the original size + DWM.DwmQueryThumbnailSourceSize(thumbnailHandle, out var sourceSize); + + if (sourceSize.Width <= 0 || sourceSize.Height <= 0) { + return null; + } + + // Calculate the location of the temp form + Rectangle windowRectangle = WindowRectangle; + Point formLocation = windowRectangle.Location; + Size borderSize = new Size(); + bool doesCaptureFit = false; + if (!Maximised) { + // Assume using it's own location + formLocation = windowRectangle.Location; + 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) { + if (!Equals(screen, Screen.PrimaryScreen)) { + workingArea.Union(screen.Bounds); + } + } + + // If the formLocation is not inside the visible area + if (!workingArea.AreRectangleCornersVisisble(windowRectangle)) { + // If none found we find the biggest screen + foreach (Screen screen in Screen.AllScreens) { + Rectangle newWindowRectangle = new Rectangle(screen.WorkingArea.Location, windowRectangle.Size); + if (workingArea.AreRectangleCornersVisisble(newWindowRectangle)) { + formLocation = screen.Bounds.Location; + doesCaptureFit = true; + break; + } + } + } else { + doesCaptureFit = true; + } + } else if (!WindowsVersion.IsWindows8OrLater) { + //GetClientRect(out windowRectangle); + GetBorderSize(out borderSize); + formLocation = new Point(windowRectangle.X - borderSize.Width, windowRectangle.Y - borderSize.Height); + } + + tempForm.Location = formLocation; + tempForm.Size = sourceSize.ToSize(); + + // Prepare rectangle to capture from the screen. + Rectangle captureRectangle = new Rectangle(formLocation.X, formLocation.Y, sourceSize.Width, sourceSize.Height); + if (Maximised) { + // Correct capture size for maximized window by offsetting the X,Y with the border size + // and subtracting the border from the size (2 times, as we move right/down for the capture without resizing) + captureRectangle.Inflate(borderSize.Width, borderSize.Height); + } else { + // TODO: Also 8.x? + if (WindowsVersion.IsWindows10OrLater) + { + captureRectangle.Inflate(Conf.Win10BorderCrop); + } + + if (autoMode) { + // check if the capture fits + if (!doesCaptureFit) + { + // if GDI is allowed.. (a screenshot won't be better than we comes if we continue) + using Process thisWindowProcess = Process; + if (!IsMetroApp && WindowCapture.IsGdiAllowed(thisWindowProcess)) { + // we return null which causes the capturing code to try another method. + return null; + } + } + } + } + // Prepare the displaying of the Thumbnail + DWM_THUMBNAIL_PROPERTIES props = new DWM_THUMBNAIL_PROPERTIES + { + Opacity = 255, + Visible = true, + Destination = new RECT(0, 0, sourceSize.Width, sourceSize.Height) + }; + DWM.DwmUpdateThumbnailProperties(thumbnailHandle, ref props); + tempForm.Show(); + tempFormShown = true; + + // Intersect with screen + captureRectangle.Intersect(capture.ScreenBounds); + + // Destination bitmap for the capture + Bitmap capturedBitmap = null; + bool frozen = false; + try { + // Check if we make a transparent capture + if (windowCaptureMode == WindowCaptureMode.AeroTransparent) { + frozen = FreezeWindow(); + // Use white, later black to capture transparent + tempForm.BackColor = Color.White; + // Make sure everything is visible + tempForm.Refresh(); + Application.DoEvents(); + + try + { + using Bitmap whiteBitmap = WindowCapture.CaptureRectangle(captureRectangle); + // Apply a white color + tempForm.BackColor = Color.Black; + // Make sure everything is visible + tempForm.Refresh(); + if (!IsMetroApp) { + // Make sure the application window is active, so the colors & buttons are right + ToForeground(); + } + // Make sure all changes are processed and visible + Application.DoEvents(); + using Bitmap blackBitmap = WindowCapture.CaptureRectangle(captureRectangle); + capturedBitmap = ApplyTransparency(blackBitmap, whiteBitmap); + } catch (Exception e) { + Log.Debug("Exception: ", e); + // Some problem occurred, cleanup and make a normal capture + if (capturedBitmap != null) { + capturedBitmap.Dispose(); + capturedBitmap = null; + } + } + } + // If no capture up till now, create a normal capture. + if (capturedBitmap == null) { + // Remove transparency, this will break the capturing + if (!autoMode) { + tempForm.BackColor = Color.FromArgb(255, Conf.DWMBackgroundColor.R, Conf.DWMBackgroundColor.G, Conf.DWMBackgroundColor.B); + } else { + Color colorizationColor = DWM.ColorizationColor; + // Modify by losing the transparency and increasing the intensity (as if the background color is white) + colorizationColor = Color.FromArgb(255, (colorizationColor.R + 255) >> 1, (colorizationColor.G + 255) >> 1, (colorizationColor.B + 255) >> 1); + tempForm.BackColor = colorizationColor; + } + // Make sure everything is visible + tempForm.Refresh(); + if (!IsMetroApp) { + // Make sure the application window is active, so the colors & buttons are right + ToForeground(); + } + // Make sure all changes are processed and visible + Application.DoEvents(); + // Capture from the screen + capturedBitmap = WindowCapture.CaptureRectangle(captureRectangle); + } + if (capturedBitmap != null) { + // Not needed for Windows 8 + if (!WindowsVersion.IsWindows8OrLater) { + // Only if the Inivalue is set, not maximized and it's not a tool window. + if (Conf.WindowCaptureRemoveCorners && !Maximised && (ExtendedWindowStyle & ExtendedWindowStyleFlags.WS_EX_TOOLWINDOW) == 0) { + // Remove corners + if (!Image.IsAlphaPixelFormat(capturedBitmap.PixelFormat)) { + Log.Debug("Changing pixelformat to Alpha for the RemoveCorners"); + Bitmap tmpBitmap = ImageHelper.Clone(capturedBitmap, PixelFormat.Format32bppArgb); + capturedBitmap.Dispose(); + capturedBitmap = tmpBitmap; + } + RemoveCorners(capturedBitmap); + } + } + } + } finally { + // Make sure to ALWAYS unfreeze!! + if (frozen) { + UnfreezeWindow(); + } + } + + capture.Image = capturedBitmap; + // Make sure the capture location is the location of the window, not the copy + capture.Location = Location; + } finally { + if (thumbnailHandle != IntPtr.Zero) { + // Unregister (cleanup), as we are finished we don't need the form or the thumbnail anymore + DWM.DwmUnregisterThumbnail(thumbnailHandle); + } + if (tempForm != null) { + if (tempFormShown) { + tempForm.Close(); + } + tempForm.Dispose(); + tempForm = null; + } + } + + return capture; + } + + /// + /// Helper method to remove the corners from a DMW capture + /// + /// The bitmap to remove the corners from. + private void RemoveCorners(Bitmap image) + { + using IFastBitmap fastBitmap = FastBitmap.Create(image); + for (int y = 0; y < Conf.WindowCornerCutShape.Count; y++) { + for (int x = 0; x < Conf.WindowCornerCutShape[y]; x++) { + fastBitmap.SetColorAt(x, y, Color.Transparent); + fastBitmap.SetColorAt(image.Width-1-x, y, Color.Transparent); + fastBitmap.SetColorAt(image.Width-1-x, image.Height-1-y, Color.Transparent); + fastBitmap.SetColorAt(x, image.Height-1-y, Color.Transparent); + } + } + } + + /// + /// Apply transparency by comparing a transparent capture with a black and white background + /// A "Math.min" makes sure there is no overflow, but this could cause the picture to have shifted colors. + /// The pictures should have been taken without differency, except for the colors. + /// + /// Bitmap with the black image + /// Bitmap with the black image + /// Bitmap with transparency + private Bitmap ApplyTransparency(Bitmap blackBitmap, Bitmap whiteBitmap) + { + using IFastBitmap targetBuffer = FastBitmap.CreateEmpty(blackBitmap.Size, PixelFormat.Format32bppArgb, Color.Transparent); + targetBuffer.SetResolution(blackBitmap.HorizontalResolution, blackBitmap.VerticalResolution); + using (IFastBitmap blackBuffer = FastBitmap.Create(blackBitmap)) + { + using IFastBitmap whiteBuffer = FastBitmap.Create(whiteBitmap); + for (int y = 0; y < blackBuffer.Height; y++) { + for (int x = 0; x < blackBuffer.Width; x++) { + Color c0 = blackBuffer.GetColorAt(x, y); + Color c1 = whiteBuffer.GetColorAt(x, y); + // Calculate alpha as double in range 0-1 + int alpha = c0.R - c1.R + 255; + if (alpha == 255) { + // Alpha == 255 means no change! + targetBuffer.SetColorAt(x, y, c0); + } else if (alpha == 0) { + // Complete transparency, use transparent pixel + targetBuffer.SetColorAt(x, y, Color.Transparent); + } else { + // Calculate original color + byte originalAlpha = (byte)Math.Min(255, alpha); + var alphaFactor = alpha/255d; + //LOG.DebugFormat("Alpha {0} & c0 {1} & c1 {2}", alpha, c0, c1); + byte originalRed = (byte)Math.Min(255, c0.R / alphaFactor); + byte originalGreen = (byte)Math.Min(255, c0.G / alphaFactor); + byte originalBlue = (byte)Math.Min(255, c0.B / alphaFactor); + Color originalColor = Color.FromArgb(originalAlpha, originalRed, originalGreen, originalBlue); + //Color originalColor = Color.FromArgb(originalAlpha, originalRed, c0.G, c0.B); + targetBuffer.SetColorAt(x, y, originalColor); + } + } + } + } + return targetBuffer.UnlockAndReturnBitmap(); + } + + /// + /// Helper method to get the window size for DWM Windows + /// + /// out Rectangle + /// bool true if it worked + private bool GetExtendedFrameBounds(out Rectangle rectangle) { + int result = DWM.DwmGetWindowAttribute(Handle, DWMWINDOWATTRIBUTE.DWMWA_EXTENDED_FRAME_BOUNDS, out RECT rect, Marshal.SizeOf(typeof(RECT))); + if (result >= 0) { + rectangle = rect.ToRectangle(); + return true; + } + rectangle = Rectangle.Empty; + return false; + } + + /// + /// Helper method to get the window size for GDI Windows + /// + /// out Rectangle + /// bool true if it worked + private bool GetClientRect(out Rectangle rectangle) { + var windowInfo = new WindowInfo(); + // Get the Window Info for this window + bool result = User32.GetWindowInfo(Handle, ref windowInfo); + rectangle = result ? windowInfo.rcClient.ToRectangle() : Rectangle.Empty; + return result; + } + + /// + /// Helper method to get the window size for GDI Windows + /// + /// out Rectangle + /// bool true if it worked + private bool GetWindowRect(out Rectangle rectangle) { + var windowInfo = new WindowInfo(); + // Get the Window Info for this window + bool result = User32.GetWindowInfo(Handle, ref windowInfo); + rectangle = result ? windowInfo.rcWindow.ToRectangle() : Rectangle.Empty; + return result; + } + + /// + /// Helper method to get the Border size for GDI Windows + /// + /// out Size + /// bool true if it worked + private bool GetBorderSize(out Size size) { + var windowInfo = new WindowInfo(); + // Get the Window Info for this window + bool result = User32.GetWindowInfo(Handle, ref windowInfo); + size = result ? new Size((int)windowInfo.cxWindowBorders, (int)windowInfo.cyWindowBorders) : Size.Empty; + return result; + } + + /// + /// Set the window as foreground window + /// + /// hWnd of the window to bring to the foreground + /// bool with true to use a trick to really bring the window to the foreground + public static void ToForeground(IntPtr handle, bool workaround = true) + { + var window = new WindowDetails(handle); + // Nothing we can do if it's not visible! + if (!window.Visible) + { + return; + } + if (window.Iconic) + { + window.Iconic = false; + while (window.Iconic) + { + Application.DoEvents(); + } + } + // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms633539(v=vs.85).aspx + if (workaround) + { + const byte alt = 0xA4; + const int extendedkey = 0x1; + const int keyup = 0x2; + // Simulate an "ALT" key press. + User32.keybd_event(alt, 0x45, extendedkey | 0, 0); + // Simulate an "ALT" key release. + User32.keybd_event(alt, 0x45, extendedkey | keyup, 0); + } + // Show window in forground. + User32.BringWindowToTop(handle); + User32.SetForegroundWindow(handle); + } + + /// + /// Set the window as foreground window + /// + /// true to use a workaround, otherwise the window might only flash + public void ToForeground(bool workaround = true) { + ToForeground(Handle, workaround); + } + + /// + /// Get the region for a window + /// + private Region GetRegion() { + using (SafeRegionHandle region = GDI32.CreateRectRgn(0, 0, 0, 0)) { + if (!region.IsInvalid) { + RegionResult result = User32.GetWindowRgn(Handle, region); + if (result != RegionResult.REGION_ERROR && result != RegionResult.REGION_NULLREGION) { + return Region.FromHrgn(region.DangerousGetHandle()); + } + } + } + return null; + } + + private bool CanFreezeOrUnfreeze(string titleOrProcessname) { + if (string.IsNullOrEmpty(titleOrProcessname)) { + return false; + } + if (titleOrProcessname.ToLower().Contains("greenshot")) { + return false; + } + + foreach (string excludeProcess in ExcludeProcessesFromFreeze) { + if (titleOrProcessname.ToLower().Contains(excludeProcess)) { + return false; + } + } + return true; + } + + /// + /// Freezes the process belonging to the window + /// Warning: Use only if no other way!! + /// + private bool FreezeWindow() { + bool frozen = false; + using (Process proc = Process.GetProcessById(ProcessId)) { + string processName = proc.ProcessName; + if (!CanFreezeOrUnfreeze(processName)) { + Log.DebugFormat("Not freezing {0}", processName); + return false; + } + if (!CanFreezeOrUnfreeze(Text)) { + Log.DebugFormat("Not freezing {0}", processName); + return false; + } + Log.DebugFormat("Freezing process: {0}", processName); + + + foreach (ProcessThread pT in proc.Threads) { + IntPtr pOpenThread = Kernel32.OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)pT.Id); + + if (pOpenThread == IntPtr.Zero) { + break; + } + frozen = true; + Kernel32.SuspendThread(pOpenThread); + pT.Dispose(); + } + } + return frozen; + } + + /// + /// Unfreeze the process belonging to the window + /// + public void UnfreezeWindow() + { + using Process proc = Process.GetProcessById(ProcessId); + string processName = proc.ProcessName; + if (!CanFreezeOrUnfreeze(processName)) { + Log.DebugFormat("Not unfreezing {0}", processName); + return; + } + if (!CanFreezeOrUnfreeze(Text)) { + Log.DebugFormat("Not unfreezing {0}", processName); + return; + } + Log.DebugFormat("Unfreezing process: {0}", processName); + + foreach (ProcessThread pT in proc.Threads) { + IntPtr pOpenThread = Kernel32.OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)pT.Id); + + if (pOpenThread == IntPtr.Zero) { + break; + } + + Kernel32.ResumeThread(pOpenThread); + } + } + + /// + /// Return an Image representing the Window! + /// As GDI+ draws it, it will be without Aero borders! + /// + public Image PrintWindow() { + Rectangle windowRect = WindowRectangle; + // Start the capture + Exception exceptionOccured = null; + Image returnImage; + using (Region region = GetRegion()) { + PixelFormat pixelFormat = PixelFormat.Format24bppRgb; + // Only use 32 bpp ARGB when the window has a region + if (region != null) { + pixelFormat = PixelFormat.Format32bppArgb; + } + returnImage = new Bitmap(windowRect.Width, windowRect.Height, pixelFormat); + using Graphics graphics = Graphics.FromImage(returnImage); + using (SafeDeviceContextHandle graphicsDc = graphics.GetSafeDeviceContext()) { + bool printSucceeded = User32.PrintWindow(Handle, graphicsDc.DangerousGetHandle(), 0x0); + if (!printSucceeded) { + // something went wrong, most likely a "0x80004005" (Acess Denied) when using UAC + exceptionOccured = User32.CreateWin32Exception("PrintWindow"); + } + } + + // Apply the region "transparency" + if (region != null && !region.IsEmpty(graphics)) { + graphics.ExcludeClip(region); + graphics.Clear(Color.Transparent); + } + + graphics.Flush(); + } + + // Return null if error + if (exceptionOccured != null) { + Log.ErrorFormat("Error calling print window: {0}", exceptionOccured.Message); + returnImage.Dispose(); + return null; + } + if (!HasParent && Maximised) { + Log.Debug("Correcting for maximalization"); + GetBorderSize(out var borderSize); + Rectangle borderRectangle = new Rectangle(borderSize.Width, borderSize.Height, windowRect.Width - (2 * borderSize.Width), windowRect.Height - (2 * borderSize.Height)); + ImageHelper.Crop(ref returnImage, ref borderRectangle); + } + return returnImage; + } + + /// + /// Constructs a new instance of this class for + /// the specified Window Handle. + /// + /// The Window Handle + public WindowDetails(IntPtr hWnd) { + Handle = hWnd; + } + + /// + /// Gets an instance of the current active foreground window + /// + /// WindowDetails of the current window + public static WindowDetails GetActiveWindow() { + IntPtr hWnd = User32.GetForegroundWindow(); + if (hWnd != IntPtr.Zero) { + if (IgnoreHandles.Contains(hWnd)) { + return GetDesktopWindow(); + } + + WindowDetails activeWindow = new WindowDetails(hWnd); + // Invisible Windows should not be active + if (!activeWindow.Visible) { + return GetDesktopWindow(); + } + return activeWindow; + } + return null; + } + + /// + /// Check if this window is Greenshot + /// + public bool IsGreenshot { + get { + try { + if (!IsMetroApp) + { + using Process thisWindowProcess = Process; + return "Greenshot".Equals(thisWindowProcess.MainModule.FileVersionInfo.ProductName); + } + } catch (Exception ex) { + Log.Warn(ex); + } + return false; + } + } + + /// + /// Gets the Desktop window + /// + /// WindowDetails for the desktop window + public static WindowDetails GetDesktopWindow() { + return new WindowDetails(User32.GetDesktopWindow()); + } + + /// + /// Get all the top level windows + /// + /// List of WindowDetails with all the top level windows + public static IList GetAllWindows() { + return GetAllWindows(null); + } + + /// + /// Get all the top level windows, with matching classname + /// + /// List WindowDetails with all the top level windows + public static IList GetAllWindows(string classname) { + return new WindowsEnumerator().GetWindows(IntPtr.Zero, classname).Items; + } + + /// + /// Recursive "find children which" + /// + /// point to check for + /// + public WindowDetails FindChildUnderPoint(Point point) { + if (!Contains(point)) { + return null; + } + var rect = WindowRectangle; + // If the mouse it at the edge, take the whole window + if (rect.X == point.X || rect.Y == point.Y || rect.Right == point.X || rect.Bottom == point.Y) + { + return this; + } + // Look into the child windows + foreach(var childWindow in Children) { + if (childWindow.Contains(point)) { + return childWindow.FindChildUnderPoint(point); + } + } + return this; + } + + /// + /// Retrieves the classname for a hWnd + /// + /// IntPtr with the windows handle + /// String with ClassName + public static string GetClassName(IntPtr hWnd) { + var classNameBuilder = new StringBuilder(260, 260); + User32.GetClassName(hWnd, classNameBuilder, classNameBuilder.Capacity); + return classNameBuilder.ToString(); + } + + /// + /// Helper method to decide if a top level window is visible + /// + /// + /// + /// + private static bool IsVisible(WindowDetails window, Rectangle screenBounds) + { + // Ignore invisible + if (!window.Visible) + { + return false; + } + // Ignore minimized + if (window.Iconic) + { + return false; + } + if (IgnoreClasses.Contains(window.ClassName)) + { + return false; + } + // On windows which are visible on the screen + var windowRect = window.WindowRectangle; + windowRect.Intersect(screenBounds); + if (windowRect.IsEmpty) + { + return false; + } + // Skip everything which is not rendered "normally", trying to fix BUG-2017 + var exWindowStyle = window.ExtendedWindowStyle; + if (!window.IsApp && !window.IsWin10App && (exWindowStyle & ExtendedWindowStyleFlags.WS_EX_NOREDIRECTIONBITMAP) != 0) + { + return false; + } + + return true; + } + + /// + /// Get all the visible top level windows + /// + /// List WindowDetails with all the visible top level windows + public static IEnumerable GetVisibleWindows() { + Rectangle screenBounds = WindowCapture.GetScreenBounds(); + foreach(var window in GetMetroApps()) { + if (IsVisible(window, screenBounds)) + { + yield return window; + } + } + foreach (var window in GetAllWindows()) + { + if (IsVisible(window, screenBounds)) + { + yield return window; + } + } + } + + /// + /// Get the WindowDetails for all Metro Apps + /// These are all Windows with Classname "Windows.UI.Core.CoreWindow" + /// + /// List WindowDetails with visible metro apps + public static IEnumerable GetMetroApps() { + // 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); + 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); + if (gutterHandle != IntPtr.Zero) { + yield return new WindowDetails(gutterHandle); + } + } + nextHandle = User32.FindWindowEx(IntPtr.Zero, nextHandle, MetroWindowsClass, null); + } + } + + /// + /// Check if the window is a top level + /// + /// WindowDetails + /// bool + private static bool IsTopLevel(WindowDetails window) + { + // 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.IsEmpty) + { + return false; + } + if (window.HasParent) + { + return false; + } + var exWindowStyle = window.ExtendedWindowStyle; + if ((exWindowStyle & ExtendedWindowStyleFlags.WS_EX_TOOLWINDOW) != 0) + { + return false; + } + // Skip everything which is not rendered "normally", trying to fix BUG-2017 + if (!window.IsApp && !window.IsWin10App && (exWindowStyle & ExtendedWindowStyleFlags.WS_EX_NOREDIRECTIONBITMAP) != 0) + { + return false; + } + // Skip preview windows, like the one from Firefox + if ((window.WindowStyle & WindowStyleFlags.WS_VISIBLE) == 0) + { + return false; + } + return window.Visible || window.Iconic; + } + + /// + /// Get all the top level windows + /// + /// List WindowDetails with all the top level windows + public static IEnumerable GetTopLevelWindows() { + foreach (var possibleTopLevel in GetMetroApps()) + { + if (IsTopLevel(possibleTopLevel)) + { + yield return possibleTopLevel; + } + } + + foreach (var possibleTopLevel in GetAllWindows()) + { + if (IsTopLevel(possibleTopLevel)) + { + yield return possibleTopLevel; + } + } + } + + /// + /// Find a window belonging to the same process as the supplied window. + /// + /// + /// + public static WindowDetails GetLinkedWindow(WindowDetails windowToLinkTo) { + int processIdSelectedWindow = windowToLinkTo.ProcessId; + foreach(var window in GetAllWindows()) { + // Ignore windows without title + if (window.Text.Length == 0) { + continue; + } + // Ignore invisible + if (!window.Visible) { + continue; + } + if (window.Handle == windowToLinkTo.Handle) { + continue; + } + if (window.Iconic) { + continue; + } + + // Windows without size + Size windowSize = window.WindowRectangle.Size; + if (windowSize.Width == 0 || windowSize.Height == 0) { + continue; + } + + if (window.ProcessId == processIdSelectedWindow) { + Log.InfoFormat("Found window {0} belonging to same process as the window {1}", window.Text, windowToLinkTo.Text); + return window; + } + } + return null; + } + + /// + /// Helper method to "active" all windows that are not in the supplied list. + /// One should preferably call "GetVisibleWindows" for the oldWindows. + /// + /// List WindowDetails with old windows + public static void ActiveNewerWindows(IEnumerable oldWindows) + { + var oldWindowsList = new List(oldWindows); + foreach(var window in GetVisibleWindows()) { + if (!oldWindowsList.Contains(window)) { + window.ToForeground(); + } + } + } + + /// + /// Get the AppLauncher + /// + /// + public static WindowDetails GetAppLauncher() { + // Only if Windows 8 (or higher) + if (AppVisibility == null) { + return null; + } + IntPtr appLauncher = User32.FindWindow(MetroApplauncherClass, null); + if (appLauncher != IntPtr.Zero) { + return new WindowDetails (appLauncher); + } + return null; + } + + /// + /// Return true if the metro-app-launcher is visible + /// + /// + public static bool IsAppLauncherVisible { + get { + if (AppVisibility != null) { + return AppVisibility.IsLauncherVisible; + } + return false; + } + } + } +} \ No newline at end of file diff --git a/GreenshotPlugin/Core/WindowsEnumerator.cs b/GreenshotPlugin/Core/WindowsEnumerator.cs new file mode 100644 index 000000000..da87e94d4 --- /dev/null +++ b/GreenshotPlugin/Core/WindowsEnumerator.cs @@ -0,0 +1,114 @@ +/* + * 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 GreenshotPlugin.UnmanagedHelpers; +using System; +using System.Collections.Generic; +using System.Text; + +namespace GreenshotPlugin.Core { + /// + /// EnumWindows wrapper for .NET + /// + public class WindowsEnumerator { + /// + /// Returns the collection of windows returned by GetWindows + /// + public IList Items { get; private set; } + + /// + /// Gets all top level windows on the system. + /// + public WindowsEnumerator GetWindows() { + GetWindows(IntPtr.Zero, null); + return this; + } + + /// + /// Gets all child windows of the specified window + /// + /// Window Handle to get children for + public WindowsEnumerator GetWindows(WindowDetails parent) + { + GetWindows(parent?.Handle ?? IntPtr.Zero, null); + return this; + } + + /// + /// Gets all child windows of the specified window + /// + /// Window Handle to get children for + /// 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); + string parentText = null; + if (hasParent) { + var title = new StringBuilder(260, 260); + User32.GetWindowText(hWndParent, title, title.Capacity); + parentText = title.ToString(); + } + + foreach (var window in Items) { + if (hasParent) { + window.Text = parentText; + window.ParentHandle = hWndParent; + } + if (classname == null || window.ClassName.Equals(classname)) { + windows.Add(window); + } + } + Items = windows; + return this; + } + + /// + /// The enum Windows callback. + /// + /// Window Handle + /// Application defined value + /// 1 to continue enumeration, 0 to stop + private int WindowEnum(IntPtr hWnd, int lParam) + { + return OnWindowEnum(hWnd) ? 1 : 0; + } + + /// + /// Called whenever a new window is about to be added + /// by the Window enumeration called from GetWindows. + /// If overriding this function, return true to continue + /// enumeration or false to stop. If you do not call + /// the base implementation the Items collection will + /// be empty. + /// + /// Window handle to add + /// True to continue enumeration, False to stop + protected bool OnWindowEnum(IntPtr hWnd) { + if (!WindowDetails.IsIgnoreHandle(hWnd)) { + Items.Add(new WindowDetails(hWnd)); + } + return true; + } + } +} diff --git a/GreenshotPlugin/Core/WindowsHelper.cs b/GreenshotPlugin/Core/WindowsHelper.cs deleted file mode 100644 index cc120df33..000000000 --- a/GreenshotPlugin/Core/WindowsHelper.cs +++ /dev/null @@ -1,1779 +0,0 @@ -/* - * 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 GreenshotPlugin.UnmanagedHelpers; -using log4net; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Drawing; -using System.Drawing.Imaging; -using System.Runtime.InteropServices; -using System.Text; -using System.Text.RegularExpressions; -using System.Windows.Forms; -using GreenshotPlugin.IniFile; -using GreenshotPlugin.Interfaces; -using GreenshotPlugin.Interop; -using GreenshotPlugin.UnmanagedHelpers.Enums; -using GreenshotPlugin.UnmanagedHelpers.Structs; - -namespace GreenshotPlugin.Core { - /// - /// EnumWindows wrapper for .NET - /// - public class WindowsEnumerator { - /// - /// Returns the collection of windows returned by GetWindows - /// - public IList Items { get; private set; } - - /// - /// Gets all top level windows on the system. - /// - public WindowsEnumerator GetWindows() { - GetWindows(IntPtr.Zero, null); - return this; - } - - /// - /// Gets all child windows of the specified window - /// - /// Window Handle to get children for - public WindowsEnumerator GetWindows(WindowDetails parent) - { - GetWindows(parent?.Handle ?? IntPtr.Zero, null); - return this; - } - - /// - /// Gets all child windows of the specified window - /// - /// Window Handle to get children for - /// 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); - string parentText = null; - if (hasParent) { - var title = new StringBuilder(260, 260); - User32.GetWindowText(hWndParent, title, title.Capacity); - parentText = title.ToString(); - } - - foreach (var window in Items) { - if (hasParent) { - window.Text = parentText; - window.ParentHandle = hWndParent; - } - if (classname == null || window.ClassName.Equals(classname)) { - windows.Add(window); - } - } - Items = windows; - return this; - } - - /// - /// The enum Windows callback. - /// - /// Window Handle - /// Application defined value - /// 1 to continue enumeration, 0 to stop - private int WindowEnum(IntPtr hWnd, int lParam) - { - if (OnWindowEnum(hWnd)) { - return 1; - } - return 0; - } - - /// - /// Called whenever a new window is about to be added - /// by the Window enumeration called from GetWindows. - /// If overriding this function, return true to continue - /// enumeration or false to stop. If you do not call - /// the base implementation the Items collection will - /// be empty. - /// - /// Window handle to add - /// True to continue enumeration, False to stop - protected virtual bool OnWindowEnum(IntPtr hWnd) { - if (!WindowDetails.IsIgnoreHandle(hWnd)) { - Items.Add(new WindowDetails(hWnd)); - } - return true; - } - } - - /// - /// Code for handling with "windows" - /// Main code is taken from vbAccelerator, location: - /// http://www.vbaccelerator.com/home/NET/Code/Libraries/Windows/Enumerating_Windows/article.asp - /// but a LOT of changes/enhancements were made to adapt it for Greenshot. - /// - /// 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"; - private static readonly IList IgnoreClasses = new List(new[] { "Progman", "Button", "Dwm" }); //"MS-SDIa" - - private static readonly ILog Log = LogManager.GetLogger(typeof(WindowDetails)); - private static readonly CoreConfiguration Conf = IniConfig.GetIniSection(); - private static readonly IList IgnoreHandles = new List(); - private static readonly IList ExcludeProcessesFromFreeze = new List(); - private static readonly IAppVisibility AppVisibility; - - static WindowDetails() { - try - { - // Only try to instantiate when Windows 8 or later. - if (Environment.OSVersion.Version.Major >= 6 && Environment.OSVersion.Version.Minor >= 2) - { - AppVisibility = COMWrapper.CreateInstance(); - } - } - catch (Exception ex) - { - Log.WarnFormat("Couldn't create instance of IAppVisibility: {0}", ex.Message); - } - } - - public static void AddProcessToExcludeFromFreeze(string processname) { - if (!ExcludeProcessesFromFreeze.Contains(processname)) { - ExcludeProcessesFromFreeze.Add(processname); - } - } - - internal static bool IsIgnoreHandle(IntPtr handle) { - return IgnoreHandles.Contains(handle); - } - - private IList _childWindows; - private IntPtr _parentHandle = IntPtr.Zero; - private WindowDetails _parent; - private bool _frozen; - - /// - /// 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); - - /// - /// This checks if the window is a Windows 10 App - /// For Windows 10 apps are hosted inside "ApplicationFrameWindow" - /// - public bool IsWin10App => FramedAppClass.Equals(ClassName); - - /// - /// Check if the window is the metro gutter (sizeable separator) - /// - public bool IsGutter => MetroGutterClass.Equals(ClassName); - - /// - /// Test if this window is for the App-Launcher - /// - public bool IsAppLauncher => MetroApplauncherClass.Equals(ClassName); - - /// - /// Check if this window is the window of a metro app - /// - public bool IsMetroApp => IsAppLauncher || IsApp; - - /// - /// To allow items to be compared, the hash code - /// is set to the Window handle, so two EnumWindowsItem - /// objects for the same Window will be equal. - /// - /// The Window Handle for this window - public override int GetHashCode() { - return Handle.ToInt32(); - } - - public override bool Equals(object right) { - return Equals(right as WindowDetails); - } - - /// - /// Compare two windows details - /// - /// - /// - public bool Equals(WindowDetails other) { - if (other is null) { - return false; - } - - if (ReferenceEquals(this, other)) { - return true; - } - - if (GetType() != other.GetType()){ - return false; - } - return other.Handle == Handle; - } - - /// - /// Check if the window has children - /// - public bool HasChildren => (_childWindows != null) && (_childWindows.Count > 0); - - /// - /// Freeze information updates - /// - public void FreezeDetails() { - _frozen = true; - } - - /// - /// Make the information update again. - /// - public void UnfreezeDetails() { - _frozen = false; - } - - /// - /// Get the file path to the exe for the process which owns this window - /// - public string ProcessPath { - get { - if (Handle == IntPtr.Zero) { - // not a valid window handle - return string.Empty; - } - // Get the process id - User32.GetWindowThreadProcessId(Handle, out var processid); - return Kernel32.GetProcessPath(processid); - } - } - - - /// - /// Get the icon belonging to the process - /// - public Image DisplayIcon { - get { - try - { - using var appIcon = GetAppIcon(Handle); - if (appIcon != null) { - return appIcon.ToBitmap(); - } - } catch (Exception ex) { - Log.WarnFormat("Couldn't get icon for window {0} due to: {1}", Text, ex.Message); - Log.Warn(ex); - } - if (IsMetroApp) { - // No method yet to get the metro icon - return null; - } - try { - return PluginUtils.GetCachedExeIcon(ProcessPath, 0); - } catch (Exception ex) { - Log.WarnFormat("Couldn't get icon for window {0} due to: {1}", Text, ex.Message); - Log.Warn(ex); - } - return null; - } - } - - /// - /// Get the icon for a hWnd - /// - /// - /// - private static Icon GetAppIcon(IntPtr hwnd) { - IntPtr iconSmall = IntPtr.Zero; - IntPtr iconBig = new IntPtr(1); - IntPtr iconSmall2 = new IntPtr(2); - - IntPtr iconHandle; - if (Conf.UseLargeIcons) { - iconHandle = User32.SendMessage(hwnd, (int)WindowsMessages.WM_GETICON, iconBig, IntPtr.Zero); - if (iconHandle == IntPtr.Zero) { - iconHandle = User32.GetClassLongWrapper(hwnd, (int)ClassLongIndex.GCL_HICON); - } - } else { - iconHandle = User32.SendMessage(hwnd, (int)WindowsMessages.WM_GETICON, iconSmall2, IntPtr.Zero); - } - if (iconHandle == IntPtr.Zero) { - iconHandle = User32.SendMessage(hwnd, (int)WindowsMessages.WM_GETICON, iconSmall, IntPtr.Zero); - } - if (iconHandle == IntPtr.Zero) { - iconHandle = User32.GetClassLongWrapper(hwnd, (int)ClassLongIndex.GCL_HICONSM); - } - if (iconHandle == IntPtr.Zero) { - iconHandle = User32.SendMessage(hwnd, (int)WindowsMessages.WM_GETICON, iconBig, IntPtr.Zero); - } - if (iconHandle == IntPtr.Zero) { - iconHandle = User32.GetClassLongWrapper(hwnd, (int)ClassLongIndex.GCL_HICON); - } - - if (iconHandle == IntPtr.Zero) { - return null; - } - - Icon icon = Icon.FromHandle(iconHandle); - - return icon; - } - - /// - /// Use this to make remove internal windows, like the mainform and the captureforms, invisible - /// - /// - public static void RegisterIgnoreHandle(IntPtr ignoreHandle) { - IgnoreHandles.Add(ignoreHandle); - } - - /// - /// Use this to remove the with RegisterIgnoreHandle registered handle - /// - /// - public static void UnregisterIgnoreHandle(IntPtr ignoreHandle) { - IgnoreHandles.Remove(ignoreHandle); - } - - public IList Children { - get { - if (_childWindows == null) { - GetChildren(); - } - return _childWindows; - } - } - - /// - /// Retrieve all windows with a certain title or classname - /// - /// - /// The regexp to look for in the title - /// The regexp to look for in the classname - /// List WindowDetails with all the found windows - private static IEnumerable FindWindow(IList windows, string titlePattern, string classnamePattern) { - Regex titleRegexp = null; - Regex classnameRegexp = null; - - if (titlePattern != null && titlePattern.Trim().Length > 0) { - titleRegexp = new Regex(titlePattern); - } - if (classnamePattern != null && classnamePattern.Trim().Length > 0) { - classnameRegexp = new Regex(classnamePattern); - } - - foreach(WindowDetails window in windows) { - if (titleRegexp != null && titleRegexp.IsMatch(window.Text)) { - yield return window; - } else if (classnameRegexp != null && classnameRegexp.IsMatch(window.ClassName)) { - yield return window; - } - } - } - - /// - /// Retrieve the child with matching classname - /// - public WindowDetails GetChild(string childClassname) { - foreach(WindowDetails child in Children) { - if (childClassname.Equals(child.ClassName)) { - return child; - } - } - return null; - } - - /// - /// Retrieve the children with matching classname - /// - public IEnumerable GetChilden(string childClassname) { - foreach (WindowDetails child in Children) { - if (childClassname.Equals(child.ClassName)) { - yield return child; - } - } - } - - public IntPtr ParentHandle { - get { - if (_parentHandle == IntPtr.Zero) { - _parentHandle = User32.GetParent(Handle); - _parent = null; - } - return _parentHandle; - } - set { - if (_parentHandle != value) { - _parentHandle = value; - _parent = null; - } - } - } - /// - /// Get the parent of the current window - /// - /// WindowDetails of the parent, or null if none - public WindowDetails GetParent() { - if (_parent == null) { - if (_parentHandle == IntPtr.Zero) { - _parentHandle = User32.GetParent(Handle); - } - if (_parentHandle != IntPtr.Zero) { - _parent = new WindowDetails(_parentHandle); - } - } - return _parent; - } - - /// - /// Retrieve all the children, this only stores the children internally. - /// One should normally use the getter "Children" - /// - public IList GetChildren() { - if (_childWindows == null) { - return GetChildren(0); - } - return _childWindows; - } - - /// - /// Retrieve all the children, this only stores the children internally, use the "Children" property for the value - /// - /// Specify how many levels we go in - public IList GetChildren(int levelsToGo) { - if (_childWindows != null) - { - return _childWindows; - } - _childWindows = new WindowsEnumerator().GetWindows(Handle, null).Items; - foreach(var childWindow in _childWindows) { - if (levelsToGo > 0) { - childWindow.GetChildren(levelsToGo-1); - } - } - return _childWindows; - } - - /// - /// Retrieve children with a certain title or classname - /// - /// The regexp to look for in the title - /// The regexp to look for in the classname - /// List WindowDetails with all the found windows, or an empty list - public IEnumerable FindChildren(string titlePattern, string classnamePattern) { - return FindWindow(Children, titlePattern, classnamePattern); - } - - /// - /// Recursing helper method for the FindPath - /// - /// List string with classnames - /// The index in the list to look for - /// WindowDetails if a match was found - private WindowDetails FindPath(IList classnames, int index) { - if (index == classnames.Count - 1) { - foreach (var foundWindow in FindChildren(null, classnames[index])) - { - return foundWindow; - } - } else { - foreach(var foundWindow in FindChildren(null, classnames[index])) - { - var resultWindow = foundWindow.FindPath(classnames, index+1); - if (resultWindow != null) - { - return resultWindow; - } - } - } - return null; - } - - /// - /// This method will find the child window according to a path of classnames. - /// Usually used for finding a certain "content" window like for the IE Browser - /// - /// List of string with classname "path" - /// true allows the search to skip a classname of the path - /// WindowDetails if found - public WindowDetails FindPath(IList classnames, bool allowSkip) { - int index = 0; - var resultWindow = FindPath(classnames, index++); - if (resultWindow == null && allowSkip) { - while(resultWindow == null && index < classnames.Count) { - resultWindow = FindPath(classnames, index); - } - } - return resultWindow; - } - - /// - /// Deep scan for a certain classname pattern - /// - /// Window to scan into - /// Classname regexp pattern - /// The first WindowDetails found - public static WindowDetails DeepScan(WindowDetails windowDetails, Regex classnamePattern) { - if (classnamePattern.IsMatch(windowDetails.ClassName)) { - return windowDetails; - } - // First loop through this level - foreach(var child in windowDetails.Children) { - if (classnamePattern.IsMatch(child.ClassName)) { - return child; - } - } - // Go into all children - foreach(var child in windowDetails.Children) { - var deepWindow = DeepScan(child, classnamePattern); - if (deepWindow != null) { - return deepWindow; - } - } - return null; - } - - /// - /// GetWindow - /// - /// The GetWindowCommand to use - /// null if nothing found, otherwise the WindowDetails instance of the "child" - public WindowDetails GetWindow(GetWindowCommand gwCommand) { - var tmphWnd = User32.GetWindow(Handle, gwCommand); - if (IntPtr.Zero == tmphWnd) { - return null; - } - var windowDetails = new WindowDetails(tmphWnd) - { - _parent = this - }; - return windowDetails; - } - - /// - /// Gets the window's handle - /// - public IntPtr Handle { get; } - - private string _text; - /// - /// Gets the window's title (caption) - /// - public string Text { - set { - _text = value; - } - get { - if (_text == null) { - var title = new StringBuilder(260, 260); - User32.GetWindowText(Handle, title, title.Capacity); - _text = title.ToString(); - } - return _text; - } - } - - private string _className; - /// - /// Gets the window's class name. - /// - public string ClassName => _className ?? (_className = GetClassName(Handle)); - - /// - /// Gets/Sets whether the window is iconic (mimimized) or not. - /// - public bool Iconic { - get { - if (IsMetroApp) { - return !Visible; - } - return User32.IsIconic(Handle) || Location.X <= -32000; - } - set { - if (value) { - User32.SendMessage(Handle, (int)WindowsMessages.WM_SYSCOMMAND, (IntPtr)User32.SC_MINIMIZE, IntPtr.Zero); - } else { - User32.SendMessage(Handle, (int)WindowsMessages.WM_SYSCOMMAND, (IntPtr)User32.SC_RESTORE, IntPtr.Zero); - } - } - } - - /// - /// Gets/Sets whether the window is maximised or not. - /// - public bool Maximised { - get { - if (IsApp) - { - if (Visible) { - Rectangle windowRectangle = WindowRectangle; - foreach (var screen in Screen.AllScreens) { - if (screen.Bounds.Contains(windowRectangle)) { - if (windowRectangle.Equals(screen.Bounds)) { - return true; - } - } - } - } - return false; - } - return User32.IsZoomed(Handle); - } - set { - if (value) { - User32.SendMessage(Handle, (int)WindowsMessages.WM_SYSCOMMAND, (IntPtr)User32.SC_MAXIMIZE, IntPtr.Zero); - } else { - User32.SendMessage(Handle, (int)WindowsMessages.WM_SYSCOMMAND, (IntPtr)User32.SC_MINIMIZE, IntPtr.Zero); - } - } - } - - /// - /// This doesn't work as good as is should, but does move the App out of the way... - /// - public void HideApp() { - User32.ShowWindow(Handle, ShowWindowCommand.Hide); - } - - /// - /// Gets whether the window is visible. - /// - public bool Visible { - get { - if (IsApp) { - Rectangle windowRectangle = WindowRectangle; - foreach (Screen screen in Screen.AllScreens) { - if (screen.Bounds.Contains(windowRectangle)) { - if (windowRectangle.Equals(screen.Bounds)) { - // Fullscreen, it's "visible" when AppVisibilityOnMonitor says yes - // Although it might be the other App, this is not "very" important - RECT rect = new RECT(screen.Bounds); - IntPtr monitor = User32.MonitorFromRect(ref rect, User32.MONITOR_DEFAULTTONULL); - if (monitor != IntPtr.Zero) { - MONITOR_APP_VISIBILITY? monitorAppVisibility = AppVisibility?.GetAppVisibilityOnMonitor(monitor); - //LOG.DebugFormat("App {0} visible: {1} on {2}", Text, monitorAppVisibility, screen.Bounds); - if (monitorAppVisibility == MONITOR_APP_VISIBILITY.MAV_APP_VISIBLE) { - return true; - } - } - } else { - // Is only partly on the screen, when this happens the app is always visible! - return true; - } - } - } - return false; - } - if (IsGutter) { - // gutter is only made available when it's visible - return true; - } - if (IsAppLauncher) { - return IsAppLauncherVisible; - } - return User32.IsWindowVisible(Handle); - } - } - - public bool HasParent { - get { - GetParent(); - return _parentHandle != IntPtr.Zero; - } - } - - public int ProcessId { - get { - User32.GetWindowThreadProcessId(Handle, out var processId); - return processId; - } - } - - public Process Process { - get { - try { - User32.GetWindowThreadProcessId(Handle, out var processId); - return Process.GetProcessById(processId); - } catch (Exception ex) { - Log.Warn(ex); - } - return null; - } - } - - /// - /// Make sure the next call of a cached value is guaranteed the real value - /// - public void Reset() { - _previousWindowRectangle = Rectangle.Empty; - } - - private Rectangle _previousWindowRectangle = Rectangle.Empty; - private long _lastWindowRectangleRetrieveTime; - private const long CacheTime = TimeSpan.TicksPerSecond * 2; - - /// - /// Gets the bounding rectangle of the window - /// - public Rectangle WindowRectangle { - get { - // 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()) - { - bool gotFrameBounds = GetExtendedFrameBounds(out windowRect); - if (IsApp) - { - // 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)); - } - } - - // 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; - } - } - - /// - /// Gets the location of the window relative to the screen. - /// - public Point Location { - get { - Rectangle tmpRectangle = WindowRectangle; - return new Point(tmpRectangle.Left, tmpRectangle.Top); - } - } - - /// - /// Gets the size of the window. - /// - public Size Size { - get { - Rectangle tmpRectangle = WindowRectangle; - return new Size(tmpRectangle.Right - tmpRectangle.Left, tmpRectangle.Bottom - tmpRectangle.Top); - } - } - - /// - /// Get the client rectangle, this is the part of the window inside the borders (drawable area) - /// - public Rectangle ClientRectangle { - get { - if (!GetClientRect(out var clientRect)) - { - Win32Error error = Win32.GetLastErrorCode(); - Log.WarnFormat("Couldn't retrieve the client rectangle for {0}, error: {1}", Text, Win32.GetMessage(error)); - } - return clientRect; - } - } - - /// - /// Check if the supplied point lies in the window - /// - /// Point with the coordinates to check - /// true if the point lies within - public bool Contains(Point p) { - return WindowRectangle.Contains(p); - } - - /// - /// Restores and Brings the window to the front, - /// assuming it is a visible application window. - /// - public void Restore() { - if (Iconic) { - User32.SendMessage(Handle, (int)WindowsMessages.WM_SYSCOMMAND, (IntPtr)User32.SC_RESTORE, IntPtr.Zero); - } - User32.BringWindowToTop(Handle); - User32.SetForegroundWindow(Handle); - // Make sure windows has time to perform the action - // TODO: this is BAD practice! - while(Iconic) { - Application.DoEvents(); - } - } - - /// - /// 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/Set the WindowPlacement - /// - public WindowPlacement WindowPlacement { - get { - var placement = WindowPlacement.Default; - User32.GetWindowPlacement(Handle, ref placement); - return placement; - } - set { - User32.SetWindowPlacement(Handle, ref value); - } - } - - /// - /// 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)); - } - } - - /// - /// Capture Window with GDI+ - /// - /// The capture to fill - /// ICapture - public ICapture CaptureGdiWindow(ICapture capture) { - Image capturedImage = PrintWindow(); - if (capturedImage != null) { - capture.Image = capturedImage; - capture.Location = Location; - return capture; - } - return null; - } - - /// - /// Capture DWM Window - /// - /// Capture to fill - /// Wanted WindowCaptureMode - /// True if auto modus is used - /// ICapture with the capture - public ICapture CaptureDwmWindow(ICapture capture, WindowCaptureMode windowCaptureMode, bool autoMode) { - IntPtr thumbnailHandle = IntPtr.Zero; - Form tempForm = null; - bool tempFormShown = false; - try { - tempForm = new Form - { - ShowInTaskbar = false, - FormBorderStyle = FormBorderStyle.None, - TopMost = true - }; - - // Register the Thumbnail - DWM.DwmRegisterThumbnail(tempForm.Handle, Handle, out thumbnailHandle); - - // Get the original size - DWM.DwmQueryThumbnailSourceSize(thumbnailHandle, out var sourceSize); - - if (sourceSize.Width <= 0 || sourceSize.Height <= 0) { - return null; - } - - // Calculate the location of the temp form - Rectangle windowRectangle = WindowRectangle; - Point formLocation = windowRectangle.Location; - Size borderSize = new Size(); - bool doesCaptureFit = false; - if (!Maximised) { - // Assume using it's own location - formLocation = windowRectangle.Location; - 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) { - if (!Equals(screen, Screen.PrimaryScreen)) { - workingArea.Union(screen.Bounds); - } - } - - // If the formLocation is not inside the visible area - if (!workingArea.AreRectangleCornersVisisble(windowRectangle)) { - // If none found we find the biggest screen - foreach (Screen screen in Screen.AllScreens) { - Rectangle newWindowRectangle = new Rectangle(screen.WorkingArea.Location, windowRectangle.Size); - if (workingArea.AreRectangleCornersVisisble(newWindowRectangle)) { - formLocation = screen.Bounds.Location; - doesCaptureFit = true; - break; - } - } - } else { - doesCaptureFit = true; - } - } else if (!WindowsVersion.IsWindows8OrLater) { - //GetClientRect(out windowRectangle); - GetBorderSize(out borderSize); - formLocation = new Point(windowRectangle.X - borderSize.Width, windowRectangle.Y - borderSize.Height); - } - - tempForm.Location = formLocation; - tempForm.Size = sourceSize.ToSize(); - - // Prepare rectangle to capture from the screen. - Rectangle captureRectangle = new Rectangle(formLocation.X, formLocation.Y, sourceSize.Width, sourceSize.Height); - if (Maximised) { - // Correct capture size for maximized window by offsetting the X,Y with the border size - // and subtracting the border from the size (2 times, as we move right/down for the capture without resizing) - captureRectangle.Inflate(borderSize.Width, borderSize.Height); - } else { - // TODO: Also 8.x? - if (WindowsVersion.IsWindows10OrLater) - { - captureRectangle.Inflate(Conf.Win10BorderCrop); - } - - if (autoMode) { - // check if the capture fits - if (!doesCaptureFit) - { - // if GDI is allowed.. (a screenshot won't be better than we comes if we continue) - using Process thisWindowProcess = Process; - if (!IsMetroApp && WindowCapture.IsGdiAllowed(thisWindowProcess)) { - // we return null which causes the capturing code to try another method. - return null; - } - } - } - } - // Prepare the displaying of the Thumbnail - DWM_THUMBNAIL_PROPERTIES props = new DWM_THUMBNAIL_PROPERTIES - { - Opacity = 255, - Visible = true, - Destination = new RECT(0, 0, sourceSize.Width, sourceSize.Height) - }; - DWM.DwmUpdateThumbnailProperties(thumbnailHandle, ref props); - tempForm.Show(); - tempFormShown = true; - - // Intersect with screen - captureRectangle.Intersect(capture.ScreenBounds); - - // Destination bitmap for the capture - Bitmap capturedBitmap = null; - bool frozen = false; - try { - // Check if we make a transparent capture - if (windowCaptureMode == WindowCaptureMode.AeroTransparent) { - frozen = FreezeWindow(); - // Use white, later black to capture transparent - tempForm.BackColor = Color.White; - // Make sure everything is visible - tempForm.Refresh(); - Application.DoEvents(); - - try - { - using Bitmap whiteBitmap = WindowCapture.CaptureRectangle(captureRectangle); - // Apply a white color - tempForm.BackColor = Color.Black; - // Make sure everything is visible - tempForm.Refresh(); - if (!IsMetroApp) { - // Make sure the application window is active, so the colors & buttons are right - ToForeground(); - } - // Make sure all changes are processed and visible - Application.DoEvents(); - using Bitmap blackBitmap = WindowCapture.CaptureRectangle(captureRectangle); - capturedBitmap = ApplyTransparency(blackBitmap, whiteBitmap); - } catch (Exception e) { - Log.Debug("Exception: ", e); - // Some problem occurred, cleanup and make a normal capture - if (capturedBitmap != null) { - capturedBitmap.Dispose(); - capturedBitmap = null; - } - } - } - // If no capture up till now, create a normal capture. - if (capturedBitmap == null) { - // Remove transparency, this will break the capturing - if (!autoMode) { - tempForm.BackColor = Color.FromArgb(255, Conf.DWMBackgroundColor.R, Conf.DWMBackgroundColor.G, Conf.DWMBackgroundColor.B); - } else { - Color colorizationColor = DWM.ColorizationColor; - // Modify by losing the transparency and increasing the intensity (as if the background color is white) - colorizationColor = Color.FromArgb(255, (colorizationColor.R + 255) >> 1, (colorizationColor.G + 255) >> 1, (colorizationColor.B + 255) >> 1); - tempForm.BackColor = colorizationColor; - } - // Make sure everything is visible - tempForm.Refresh(); - if (!IsMetroApp) { - // Make sure the application window is active, so the colors & buttons are right - ToForeground(); - } - // Make sure all changes are processed and visible - Application.DoEvents(); - // Capture from the screen - capturedBitmap = WindowCapture.CaptureRectangle(captureRectangle); - } - if (capturedBitmap != null) { - // Not needed for Windows 8 - if (!WindowsVersion.IsWindows8OrLater) { - // Only if the Inivalue is set, not maximized and it's not a tool window. - if (Conf.WindowCaptureRemoveCorners && !Maximised && (ExtendedWindowStyle & ExtendedWindowStyleFlags.WS_EX_TOOLWINDOW) == 0) { - // Remove corners - if (!Image.IsAlphaPixelFormat(capturedBitmap.PixelFormat)) { - Log.Debug("Changing pixelformat to Alpha for the RemoveCorners"); - Bitmap tmpBitmap = ImageHelper.Clone(capturedBitmap, PixelFormat.Format32bppArgb); - capturedBitmap.Dispose(); - capturedBitmap = tmpBitmap; - } - RemoveCorners(capturedBitmap); - } - } - } - } finally { - // Make sure to ALWAYS unfreeze!! - if (frozen) { - UnfreezeWindow(); - } - } - - capture.Image = capturedBitmap; - // Make sure the capture location is the location of the window, not the copy - capture.Location = Location; - } finally { - if (thumbnailHandle != IntPtr.Zero) { - // Unregister (cleanup), as we are finished we don't need the form or the thumbnail anymore - DWM.DwmUnregisterThumbnail(thumbnailHandle); - } - if (tempForm != null) { - if (tempFormShown) { - tempForm.Close(); - } - tempForm.Dispose(); - tempForm = null; - } - } - - return capture; - } - - /// - /// Helper method to remove the corners from a DMW capture - /// - /// The bitmap to remove the corners from. - private void RemoveCorners(Bitmap image) - { - using IFastBitmap fastBitmap = FastBitmap.Create(image); - for (int y = 0; y < Conf.WindowCornerCutShape.Count; y++) { - for (int x = 0; x < Conf.WindowCornerCutShape[y]; x++) { - fastBitmap.SetColorAt(x, y, Color.Transparent); - fastBitmap.SetColorAt(image.Width-1-x, y, Color.Transparent); - fastBitmap.SetColorAt(image.Width-1-x, image.Height-1-y, Color.Transparent); - fastBitmap.SetColorAt(x, image.Height-1-y, Color.Transparent); - } - } - } - - /// - /// Apply transparency by comparing a transparent capture with a black and white background - /// A "Math.min" makes sure there is no overflow, but this could cause the picture to have shifted colors. - /// The pictures should have been taken without differency, except for the colors. - /// - /// Bitmap with the black image - /// Bitmap with the black image - /// Bitmap with transparency - private Bitmap ApplyTransparency(Bitmap blackBitmap, Bitmap whiteBitmap) - { - using IFastBitmap targetBuffer = FastBitmap.CreateEmpty(blackBitmap.Size, PixelFormat.Format32bppArgb, Color.Transparent); - targetBuffer.SetResolution(blackBitmap.HorizontalResolution, blackBitmap.VerticalResolution); - using (IFastBitmap blackBuffer = FastBitmap.Create(blackBitmap)) - { - using IFastBitmap whiteBuffer = FastBitmap.Create(whiteBitmap); - for (int y = 0; y < blackBuffer.Height; y++) { - for (int x = 0; x < blackBuffer.Width; x++) { - Color c0 = blackBuffer.GetColorAt(x, y); - Color c1 = whiteBuffer.GetColorAt(x, y); - // Calculate alpha as double in range 0-1 - int alpha = c0.R - c1.R + 255; - if (alpha == 255) { - // Alpha == 255 means no change! - targetBuffer.SetColorAt(x, y, c0); - } else if (alpha == 0) { - // Complete transparency, use transparent pixel - targetBuffer.SetColorAt(x, y, Color.Transparent); - } else { - // Calculate original color - byte originalAlpha = (byte)Math.Min(255, alpha); - var alphaFactor = alpha/255d; - //LOG.DebugFormat("Alpha {0} & c0 {1} & c1 {2}", alpha, c0, c1); - byte originalRed = (byte)Math.Min(255, c0.R / alphaFactor); - byte originalGreen = (byte)Math.Min(255, c0.G / alphaFactor); - byte originalBlue = (byte)Math.Min(255, c0.B / alphaFactor); - Color originalColor = Color.FromArgb(originalAlpha, originalRed, originalGreen, originalBlue); - //Color originalColor = Color.FromArgb(originalAlpha, originalRed, c0.G, c0.B); - targetBuffer.SetColorAt(x, y, originalColor); - } - } - } - } - return targetBuffer.UnlockAndReturnBitmap(); - } - - /// - /// Helper method to get the window size for DWM Windows - /// - /// out Rectangle - /// bool true if it worked - private bool GetExtendedFrameBounds(out Rectangle rectangle) { - int result = DWM.DwmGetWindowAttribute(Handle, (int)DWMWINDOWATTRIBUTE.DWMWA_EXTENDED_FRAME_BOUNDS, out var rect, Marshal.SizeOf(typeof(RECT))); - if (result >= 0) { - rectangle = rect.ToRectangle(); - return true; - } - rectangle = Rectangle.Empty; - return false; - } - - /// - /// Helper method to get the window size for GDI Windows - /// - /// out Rectangle - /// bool true if it worked - private bool GetClientRect(out Rectangle rectangle) { - var windowInfo = new WindowInfo(); - // Get the Window Info for this window - bool result = User32.GetWindowInfo(Handle, ref windowInfo); - rectangle = result ? windowInfo.rcClient.ToRectangle() : Rectangle.Empty; - return result; - } - - /// - /// Helper method to get the window size for GDI Windows - /// - /// out Rectangle - /// bool true if it worked - private bool GetWindowRect(out Rectangle rectangle) { - var windowInfo = new WindowInfo(); - // Get the Window Info for this window - bool result = User32.GetWindowInfo(Handle, ref windowInfo); - rectangle = result ? windowInfo.rcWindow.ToRectangle() : Rectangle.Empty; - return result; - } - - /// - /// Helper method to get the Border size for GDI Windows - /// - /// out Size - /// bool true if it worked - private bool GetBorderSize(out Size size) { - var windowInfo = new WindowInfo(); - // Get the Window Info for this window - bool result = User32.GetWindowInfo(Handle, ref windowInfo); - size = result ? new Size((int)windowInfo.cxWindowBorders, (int)windowInfo.cyWindowBorders) : Size.Empty; - return result; - } - - /// - /// Set the window as foreground window - /// - /// hWnd of the window to bring to the foreground - /// bool with true to use a trick to really bring the window to the foreground - public static void ToForeground(IntPtr handle, bool workaround = true) - { - var window = new WindowDetails(handle); - // Nothing we can do if it's not visible! - if (!window.Visible) - { - return; - } - if (window.Iconic) - { - window.Iconic = false; - while (window.Iconic) - { - Application.DoEvents(); - } - } - // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms633539(v=vs.85).aspx - if (workaround) - { - const byte alt = 0xA4; - const int extendedkey = 0x1; - const int keyup = 0x2; - // Simulate an "ALT" key press. - User32.keybd_event(alt, 0x45, extendedkey | 0, 0); - // Simulate an "ALT" key release. - User32.keybd_event(alt, 0x45, extendedkey | keyup, 0); - } - // Show window in forground. - User32.BringWindowToTop(handle); - User32.SetForegroundWindow(handle); - } - - /// - /// Set the window as foreground window - /// - /// true to use a workaround, otherwise the window might only flash - public void ToForeground(bool workaround = true) { - ToForeground(Handle, workaround); - } - - /// - /// Get the region for a window - /// - private Region GetRegion() { - using (SafeRegionHandle region = GDI32.CreateRectRgn(0, 0, 0, 0)) { - if (!region.IsInvalid) { - RegionResult result = User32.GetWindowRgn(Handle, region); - if (result != RegionResult.REGION_ERROR && result != RegionResult.REGION_NULLREGION) { - return Region.FromHrgn(region.DangerousGetHandle()); - } - } - } - return null; - } - - private bool CanFreezeOrUnfreeze(string titleOrProcessname) { - if (string.IsNullOrEmpty(titleOrProcessname)) { - return false; - } - if (titleOrProcessname.ToLower().Contains("greenshot")) { - return false; - } - - foreach (string excludeProcess in ExcludeProcessesFromFreeze) { - if (titleOrProcessname.ToLower().Contains(excludeProcess)) { - return false; - } - } - return true; - } - - /// - /// Freezes the process belonging to the window - /// Warning: Use only if no other way!! - /// - private bool FreezeWindow() { - bool frozen = false; - using (Process proc = Process.GetProcessById(ProcessId)) { - string processName = proc.ProcessName; - if (!CanFreezeOrUnfreeze(processName)) { - Log.DebugFormat("Not freezing {0}", processName); - return false; - } - if (!CanFreezeOrUnfreeze(Text)) { - Log.DebugFormat("Not freezing {0}", processName); - return false; - } - Log.DebugFormat("Freezing process: {0}", processName); - - - foreach (ProcessThread pT in proc.Threads) { - IntPtr pOpenThread = Kernel32.OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)pT.Id); - - if (pOpenThread == IntPtr.Zero) { - break; - } - frozen = true; - Kernel32.SuspendThread(pOpenThread); - pT.Dispose(); - } - } - return frozen; - } - - /// - /// Unfreeze the process belonging to the window - /// - public void UnfreezeWindow() - { - using Process proc = Process.GetProcessById(ProcessId); - string processName = proc.ProcessName; - if (!CanFreezeOrUnfreeze(processName)) { - Log.DebugFormat("Not unfreezing {0}", processName); - return; - } - if (!CanFreezeOrUnfreeze(Text)) { - Log.DebugFormat("Not unfreezing {0}", processName); - return; - } - Log.DebugFormat("Unfreezing process: {0}", processName); - - foreach (ProcessThread pT in proc.Threads) { - IntPtr pOpenThread = Kernel32.OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)pT.Id); - - if (pOpenThread == IntPtr.Zero) { - break; - } - - Kernel32.ResumeThread(pOpenThread); - } - } - - /// - /// Return an Image representing the Window! - /// As GDI+ draws it, it will be without Aero borders! - /// - public Image PrintWindow() { - Rectangle windowRect = WindowRectangle; - // Start the capture - Exception exceptionOccured = null; - Image returnImage; - using (Region region = GetRegion()) { - PixelFormat pixelFormat = PixelFormat.Format24bppRgb; - // Only use 32 bpp ARGB when the window has a region - if (region != null) { - pixelFormat = PixelFormat.Format32bppArgb; - } - returnImage = new Bitmap(windowRect.Width, windowRect.Height, pixelFormat); - using Graphics graphics = Graphics.FromImage(returnImage); - using (SafeDeviceContextHandle graphicsDc = graphics.GetSafeDeviceContext()) { - bool printSucceeded = User32.PrintWindow(Handle, graphicsDc.DangerousGetHandle(), 0x0); - if (!printSucceeded) { - // something went wrong, most likely a "0x80004005" (Acess Denied) when using UAC - exceptionOccured = User32.CreateWin32Exception("PrintWindow"); - } - } - - // Apply the region "transparency" - if (region != null && !region.IsEmpty(graphics)) { - graphics.ExcludeClip(region); - graphics.Clear(Color.Transparent); - } - - graphics.Flush(); - } - - // Return null if error - if (exceptionOccured != null) { - Log.ErrorFormat("Error calling print window: {0}", exceptionOccured.Message); - returnImage.Dispose(); - return null; - } - if (!HasParent && Maximised) { - Log.Debug("Correcting for maximalization"); - GetBorderSize(out var borderSize); - Rectangle borderRectangle = new Rectangle(borderSize.Width, borderSize.Height, windowRect.Width - (2 * borderSize.Width), windowRect.Height - (2 * borderSize.Height)); - ImageHelper.Crop(ref returnImage, ref borderRectangle); - } - return returnImage; - } - - /// - /// Constructs a new instance of this class for - /// the specified Window Handle. - /// - /// The Window Handle - public WindowDetails(IntPtr hWnd) { - Handle = hWnd; - } - - /// - /// Gets an instance of the current active foreground window - /// - /// WindowDetails of the current window - public static WindowDetails GetActiveWindow() { - IntPtr hWnd = User32.GetForegroundWindow(); - if (hWnd != IntPtr.Zero) { - if (IgnoreHandles.Contains(hWnd)) { - return GetDesktopWindow(); - } - - WindowDetails activeWindow = new WindowDetails(hWnd); - // Invisible Windows should not be active - if (!activeWindow.Visible) { - return GetDesktopWindow(); - } - return activeWindow; - } - return null; - } - - /// - /// Check if this window is Greenshot - /// - public bool IsGreenshot { - get { - try { - if (!IsMetroApp) - { - using Process thisWindowProcess = Process; - return "Greenshot".Equals(thisWindowProcess.MainModule.FileVersionInfo.ProductName); - } - } catch (Exception ex) { - Log.Warn(ex); - } - return false; - } - } - - /// - /// Gets the Desktop window - /// - /// WindowDetails for the desktop window - public static WindowDetails GetDesktopWindow() { - return new WindowDetails(User32.GetDesktopWindow()); - } - - /// - /// Get all the top level windows - /// - /// List of WindowDetails with all the top level windows - public static IList GetAllWindows() { - return GetAllWindows(null); - } - - /// - /// Get all the top level windows, with matching classname - /// - /// List WindowDetails with all the top level windows - public static IList GetAllWindows(string classname) { - return new WindowsEnumerator().GetWindows(IntPtr.Zero, classname).Items; - } - - /// - /// Recursive "find children which" - /// - /// point to check for - /// - public WindowDetails FindChildUnderPoint(Point point) { - if (!Contains(point)) { - return null; - } - var rect = WindowRectangle; - // If the mouse it at the edge, take the whole window - if (rect.X == point.X || rect.Y == point.Y || rect.Right == point.X || rect.Bottom == point.Y) - { - return this; - } - // Look into the child windows - foreach(var childWindow in Children) { - if (childWindow.Contains(point)) { - return childWindow.FindChildUnderPoint(point); - } - } - return this; - } - - /// - /// Retrieves the classname for a hWnd - /// - /// IntPtr with the windows handle - /// String with ClassName - public static string GetClassName(IntPtr hWnd) { - var classNameBuilder = new StringBuilder(260, 260); - User32.GetClassName(hWnd, classNameBuilder, classNameBuilder.Capacity); - return classNameBuilder.ToString(); - } - - /// - /// Helper method to decide if a top level window is visible - /// - /// - /// - /// - private static bool IsVisible(WindowDetails window, Rectangle screenBounds) - { - // Ignore invisible - if (!window.Visible) - { - return false; - } - // Ignore minizied - if (window.Iconic) - { - return false; - } - if (IgnoreClasses.Contains(window.ClassName)) - { - return false; - } - // On windows which are visible on the screen - var windowRect = window.WindowRectangle; - windowRect.Intersect(screenBounds); - if (windowRect.IsEmpty) - { - return false; - } - // Skip everything which is not rendered "normally", trying to fix BUG-2017 - var exWindowStyle = window.ExtendedWindowStyle; - if (!window.IsApp && !window.IsWin10App && (exWindowStyle & ExtendedWindowStyleFlags.WS_EX_NOREDIRECTIONBITMAP) != 0) - { - return false; - } - - return true; - } - - /// - /// Get all the visible top level windows - /// - /// List WindowDetails with all the visible top level windows - public static IEnumerable GetVisibleWindows() { - Rectangle screenBounds = WindowCapture.GetScreenBounds(); - foreach(var window in GetMetroApps()) { - if (IsVisible(window, screenBounds)) - { - yield return window; - } - } - foreach (var window in GetAllWindows()) - { - if (IsVisible(window, screenBounds)) - { - yield return window; - } - } - } - - /// - /// Get the WindowDetails for all Metro Apps - /// These are all Windows with Classname "Windows.UI.Core.CoreWindow" - /// - /// List WindowDetails with visible metro apps - public static IEnumerable GetMetroApps() { - // 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); - 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); - if (gutterHandle != IntPtr.Zero) { - yield return new WindowDetails(gutterHandle); - } - } - nextHandle = User32.FindWindowEx(IntPtr.Zero, nextHandle, MetroWindowsClass, null); - } - } - - /// - /// Check if the window is a top level - /// - /// WindowDetails - /// bool - private static bool IsTopLevel(WindowDetails window) - { - // 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.IsEmpty) - { - return false; - } - if (window.HasParent) - { - return false; - } - var exWindowStyle = window.ExtendedWindowStyle; - if ((exWindowStyle & ExtendedWindowStyleFlags.WS_EX_TOOLWINDOW) != 0) - { - return false; - } - // Skip everything which is not rendered "normally", trying to fix BUG-2017 - if (!window.IsApp && !window.IsWin10App && (exWindowStyle & ExtendedWindowStyleFlags.WS_EX_NOREDIRECTIONBITMAP) != 0) - { - return false; - } - // Skip preview windows, like the one from Firefox - if ((window.WindowStyle & WindowStyleFlags.WS_VISIBLE) == 0) - { - return false; - } - return window.Visible || window.Iconic; - } - - /// - /// Get all the top level windows - /// - /// List WindowDetails with all the top level windows - public static IEnumerable GetTopLevelWindows() { - foreach (var possibleTopLevel in GetMetroApps()) - { - if (IsTopLevel(possibleTopLevel)) - { - yield return possibleTopLevel; - } - } - - foreach (var possibleTopLevel in GetAllWindows()) - { - if (IsTopLevel(possibleTopLevel)) - { - yield return possibleTopLevel; - } - } - } - - /// - /// Find a window belonging to the same process as the supplied window. - /// - /// - /// - public static WindowDetails GetLinkedWindow(WindowDetails windowToLinkTo) { - int processIdSelectedWindow = windowToLinkTo.ProcessId; - foreach(var window in GetAllWindows()) { - // Ignore windows without title - if (window.Text.Length == 0) { - continue; - } - // Ignore invisible - if (!window.Visible) { - continue; - } - if (window.Handle == windowToLinkTo.Handle) { - continue; - } - if (window.Iconic) { - continue; - } - - // Windows without size - Size windowSize = window.WindowRectangle.Size; - if (windowSize.Width == 0 || windowSize.Height == 0) { - continue; - } - - if (window.ProcessId == processIdSelectedWindow) { - Log.InfoFormat("Found window {0} belonging to same process as the window {1}", window.Text, windowToLinkTo.Text); - return window; - } - } - return null; - } - - /// - /// Helper method to "active" all windows that are not in the supplied list. - /// One should preferably call "GetVisibleWindows" for the oldWindows. - /// - /// List WindowDetails with old windows - public static void ActiveNewerWindows(IEnumerable oldWindows) - { - var oldWindowsList = new List(oldWindows); - foreach(var window in GetVisibleWindows()) { - if (!oldWindowsList.Contains(window)) { - window.ToForeground(); - } - } - } - - /// - /// Get the AppLauncher - /// - /// - public static WindowDetails GetAppLauncher() { - // Only if Windows 8 (or higher) - if (AppVisibility == null) { - return null; - } - IntPtr appLauncher = User32.FindWindow(MetroApplauncherClass, null); - if (appLauncher != IntPtr.Zero) { - return new WindowDetails (appLauncher); - } - return null; - } - - /// - /// Return true if the metro-app-launcher is visible - /// - /// - public static bool IsAppLauncherVisible { - get { - if (AppVisibility != null) { - return AppVisibility.IsLauncherVisible; - } - return false; - } - } - } -} diff --git a/GreenshotPlugin/UnmanagedHelpers/Enums/DWM.cs b/GreenshotPlugin/UnmanagedHelpers/DWM.cs similarity index 55% rename from GreenshotPlugin/UnmanagedHelpers/Enums/DWM.cs rename to GreenshotPlugin/UnmanagedHelpers/DWM.cs index 0d8ad5c8e..84bee66e4 100644 --- a/GreenshotPlugin/UnmanagedHelpers/Enums/DWM.cs +++ b/GreenshotPlugin/UnmanagedHelpers/DWM.cs @@ -22,79 +22,17 @@ using System; using System.Drawing; using System.Runtime.InteropServices; +using GreenshotPlugin.Core; +using GreenshotPlugin.UnmanagedHelpers.Enums; using GreenshotPlugin.UnmanagedHelpers.Structs; using Microsoft.Win32; -namespace GreenshotPlugin.UnmanagedHelpers.Enums { +namespace GreenshotPlugin.UnmanagedHelpers { - // See: http://msdn.microsoft.com/en-us/library/aa969502(v=vs.85).aspx - [StructLayout(LayoutKind.Sequential)] - public struct DWM_THUMBNAIL_PROPERTIES { - // A bitwise combination of DWM thumbnail constant values that indicates which members of this structure are set. - public int dwFlags; - // The area in the destination window where the thumbnail will be rendered. - public RECT rcDestination; - // The region of the source window to use as the thumbnail. By default, the entire window is used as the thumbnail. - public RECT rcSource; - // The opacity with which to render the thumbnail. 0 is fully transparent while 255 is fully opaque. The default value is 255. - public byte opacity; - // TRUE to make the thumbnail visible; otherwise, FALSE. The default is FALSE. - public bool fVisible; - // TRUE to use only the thumbnail source's client area; otherwise, FALSE. The default is FALSE. - public bool fSourceClientAreaOnly; - public RECT Destination { - set { - dwFlags |= DWM_TNP_RECTDESTINATION; - rcDestination = value; - } - } - public RECT Source { - set { - dwFlags |= DWM_TNP_RECTSOURCE; - rcSource = value; - } - } - public byte Opacity { - set { - dwFlags |= DWM_TNP_OPACITY; - opacity = value; - } - } - public bool Visible { - set { - dwFlags |= DWM_TNP_VISIBLE; - fVisible = value; - } - } - public bool SourceClientAreaOnly { - set { - dwFlags |= DWM_TNP_SOURCECLIENTAREAONLY; - fSourceClientAreaOnly = value; - } - } - // A value for the rcDestination member has been specified. - public const int DWM_TNP_RECTDESTINATION = 0x00000001; - // A value for the rcSource member has been specified. - public const int DWM_TNP_RECTSOURCE = 0x00000002; - // A value for the opacity member has been specified. - public const int DWM_TNP_OPACITY = 0x00000004; - // A value for the fVisible member has been specified. - public const int DWM_TNP_VISIBLE = 0x00000008; - // A value for the fSourceClientAreaOnly member has been specified. - public const int DWM_TNP_SOURCECLIENTAREAONLY = 0x00000010; - } - - [StructLayout(LayoutKind.Sequential)] - public struct DWM_BLURBEHIND { - public DWM_BB dwFlags; - public bool fEnable; - public IntPtr hRgnBlur; - public bool fTransitionOnMaximized; - } - /// + /// /// Description of DWM. /// - public class DWM { + public static class DWM { public static readonly uint DWM_EC_DISABLECOMPOSITION = 0; public static readonly uint DWM_EC_ENABLECOMPOSITION = 1; @@ -112,9 +50,11 @@ namespace GreenshotPlugin.UnmanagedHelpers.Enums { [DllImport("dwmapi", SetLastError = true)] public static extern int DwmIsCompositionEnabled(out bool enabled); [DllImport("dwmapi", SetLastError = true)] - public static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, out RECT lpRect, int size); + public static extern int DwmGetWindowAttribute(IntPtr hWnd, DWMWINDOWATTRIBUTE dwAttribute, out RECT lpRect, int size); + [DllImport("dwmapi", SetLastError = true)] + public static extern int DwmGetWindowAttribute(IntPtr hWnd, DWMWINDOWATTRIBUTE dwAttribute, out bool pvAttribute, int cbAttribute); [DllImport("dwmapi", SetLastError = true)] - public static extern int DwmEnableBlurBehindWindow(IntPtr hwnd, ref DWM_BLURBEHIND blurBehind); + public static extern int DwmEnableBlurBehindWindow(IntPtr hWnd, ref DWM_BLURBEHIND blurBehind); [DllImport("dwmapi", SetLastError = true)] public static extern uint DwmEnableComposition(uint uCompositionAction); @@ -129,6 +69,22 @@ namespace GreenshotPlugin.UnmanagedHelpers.Enums { private const string COLORIZATION_COLOR_KEY = @"SOFTWARE\Microsoft\Windows\DWM"; /// + /// Checks if the window is cloaked, this should solve some issues with the window selection code + /// + /// IntPtr as hWmd + /// bool + public static bool IsWindowCloaked(IntPtr hWnd) + { + if (WindowsVersion.IsWindows8OrLater) + { + return false; + } + + DwmGetWindowAttribute(hWnd, DWMWINDOWATTRIBUTE.DWMWA_CLOAKED, out bool isCloaked, sizeof(bool)); + return isCloaked; + } + + /// /// Helper method for an easy DWM check /// /// bool true if DWM is available AND active @@ -136,10 +92,10 @@ namespace GreenshotPlugin.UnmanagedHelpers.Enums { // 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 (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor == 2) { + if (WindowsVersion.IsWindows8OrLater) { return true; } - if (Environment.OSVersion.Version.Major >= 6) { + if (WindowsVersion.IsWindowsVistaOrLater) { DwmIsCompositionEnabled(out var dwmEnabled); return dwmEnabled; } diff --git a/GreenshotPlugin/UnmanagedHelpers/Enums/DWMWINDOWATTRIBUTE.cs b/GreenshotPlugin/UnmanagedHelpers/Enums/DWMWINDOWATTRIBUTE.cs index b0ad45084..0a8491806 100644 --- a/GreenshotPlugin/UnmanagedHelpers/Enums/DWMWINDOWATTRIBUTE.cs +++ b/GreenshotPlugin/UnmanagedHelpers/Enums/DWMWINDOWATTRIBUTE.cs @@ -1,9 +1,31 @@ -using System.Diagnostics.CodeAnalysis; +/* + * 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.Diagnostics.CodeAnalysis; namespace GreenshotPlugin.UnmanagedHelpers.Enums { [SuppressMessage("ReSharper", "InconsistentNaming")] - public enum DWMWINDOWATTRIBUTE { + public enum DWMWINDOWATTRIBUTE : uint + { DWMWA_NCRENDERING_ENABLED = 1, DWMWA_NCRENDERING_POLICY, DWMWA_TRANSITIONS_FORCEDISABLED, diff --git a/GreenshotPlugin/UnmanagedHelpers/Enums/DWM_BB.cs b/GreenshotPlugin/UnmanagedHelpers/Enums/DWM_BB.cs index 416679286..c794d251c 100644 --- a/GreenshotPlugin/UnmanagedHelpers/Enums/DWM_BB.cs +++ b/GreenshotPlugin/UnmanagedHelpers/Enums/DWM_BB.cs @@ -1,4 +1,25 @@ -using System; +/* + * 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; using System.Diagnostics.CodeAnalysis; namespace GreenshotPlugin.UnmanagedHelpers.Enums diff --git a/GreenshotPlugin/UnmanagedHelpers/Enums/DWM_BLURBEHIND.cs b/GreenshotPlugin/UnmanagedHelpers/Enums/DWM_BLURBEHIND.cs new file mode 100644 index 000000000..f5629d9b4 --- /dev/null +++ b/GreenshotPlugin/UnmanagedHelpers/Enums/DWM_BLURBEHIND.cs @@ -0,0 +1,34 @@ +/* + * 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; +using System.Runtime.InteropServices; + +namespace GreenshotPlugin.UnmanagedHelpers.Enums +{ + [StructLayout(LayoutKind.Sequential)] + public struct DWM_BLURBEHIND { + public DWM_BB dwFlags; + public bool fEnable; + public IntPtr hRgnBlur; + public bool fTransitionOnMaximized; + } +} \ No newline at end of file diff --git a/GreenshotPlugin/UnmanagedHelpers/Enums/DWM_THUMBNAIL_PROPERTIES.cs b/GreenshotPlugin/UnmanagedHelpers/Enums/DWM_THUMBNAIL_PROPERTIES.cs new file mode 100644 index 000000000..73dee612b --- /dev/null +++ b/GreenshotPlugin/UnmanagedHelpers/Enums/DWM_THUMBNAIL_PROPERTIES.cs @@ -0,0 +1,85 @@ +/* + * 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.Runtime.InteropServices; +using GreenshotPlugin.UnmanagedHelpers.Structs; + +namespace GreenshotPlugin.UnmanagedHelpers.Enums +{ + /// + /// See: http://msdn.microsoft.com/en-us/library/aa969502(v=vs.85).aspx + /// + [StructLayout(LayoutKind.Sequential)] + public struct DWM_THUMBNAIL_PROPERTIES { + // A bitwise combination of DWM thumbnail constant values that indicates which members of this structure are set. + public int dwFlags; + // The area in the destination window where the thumbnail will be rendered. + public RECT rcDestination; + // The region of the source window to use as the thumbnail. By default, the entire window is used as the thumbnail. + public RECT rcSource; + // The opacity with which to render the thumbnail. 0 is fully transparent while 255 is fully opaque. The default value is 255. + public byte opacity; + // TRUE to make the thumbnail visible; otherwise, FALSE. The default is FALSE. + public bool fVisible; + // TRUE to use only the thumbnail source's client area; otherwise, FALSE. The default is FALSE. + public bool fSourceClientAreaOnly; + public RECT Destination { + set { + dwFlags |= DWM_TNP_RECTDESTINATION; + rcDestination = value; + } + } + public RECT Source { + set { + dwFlags |= DWM_TNP_RECTSOURCE; + rcSource = value; + } + } + public byte Opacity { + set { + dwFlags |= DWM_TNP_OPACITY; + opacity = value; + } + } + public bool Visible { + set { + dwFlags |= DWM_TNP_VISIBLE; + fVisible = value; + } + } + public bool SourceClientAreaOnly { + set { + dwFlags |= DWM_TNP_SOURCECLIENTAREAONLY; + fSourceClientAreaOnly = value; + } + } + // A value for the rcDestination member has been specified. + public const int DWM_TNP_RECTDESTINATION = 0x00000001; + // A value for the rcSource member has been specified. + public const int DWM_TNP_RECTSOURCE = 0x00000002; + // A value for the opacity member has been specified. + public const int DWM_TNP_OPACITY = 0x00000004; + // A value for the fVisible member has been specified. + public const int DWM_TNP_VISIBLE = 0x00000008; + // A value for the fSourceClientAreaOnly member has been specified. + public const int DWM_TNP_SOURCECLIENTAREAONLY = 0x00000010; + } +} \ No newline at end of file diff --git a/GreenshotPlugin/UnmanagedHelpers/Enums/SYSCOLOR.cs b/GreenshotPlugin/UnmanagedHelpers/Enums/SYSCOLOR.cs index 39f5d506c..a6c535462 100644 --- a/GreenshotPlugin/UnmanagedHelpers/Enums/SYSCOLOR.cs +++ b/GreenshotPlugin/UnmanagedHelpers/Enums/SYSCOLOR.cs @@ -1,4 +1,25 @@ -using System.Diagnostics.CodeAnalysis; +/* + * 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.Diagnostics.CodeAnalysis; namespace GreenshotPlugin.UnmanagedHelpers.Enums { diff --git a/GreenshotPlugin/UnmanagedHelpers/User32.cs b/GreenshotPlugin/UnmanagedHelpers/User32.cs index 9b9e6b80b..3c5a90a71 100644 --- a/GreenshotPlugin/UnmanagedHelpers/User32.cs +++ b/GreenshotPlugin/UnmanagedHelpers/User32.cs @@ -116,36 +116,36 @@ namespace GreenshotPlugin.UnmanagedHelpers { public static extern IntPtr GetClassLongPtr(IntPtr hWnd, int nIndex); [DllImport("user32", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool PrintWindow(IntPtr hwnd, IntPtr hDC, uint nFlags); + public static extern bool PrintWindow(IntPtr hwnd, IntPtr hDc, uint nFlags); [DllImport("user32", CharSet=CharSet.Unicode, SetLastError=true)] public static extern IntPtr SendMessage(IntPtr hWnd, uint wMsg, IntPtr wParam, IntPtr lParam); [DllImport("user32", SetLastError = true)] public static extern IntPtr SendMessage(IntPtr hWnd, uint wMsg, IntPtr wParam, [MarshalAs(UnmanagedType.LPWStr)] string lParam); [DllImport("user32", SetLastError = true, EntryPoint = "GetWindowLong")] - public static extern int GetWindowLong(IntPtr hwnd, int index); + public static extern int GetWindowLong(IntPtr hWnd, int index); [DllImport("user32", SetLastError = true, EntryPoint = "GetWindowLongPtr")] - public static extern IntPtr GetWindowLongPtr(IntPtr hwnd, int nIndex); + public static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex); [DllImport("user32", CharSet = CharSet.Unicode, SetLastError = true)] public static extern int SetWindowLong(IntPtr hWnd, int index, int styleFlags); [DllImport("user32", SetLastError = true, EntryPoint = "SetWindowLongPtr")] public static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int index, IntPtr styleFlags); [DllImport("user32", SetLastError = true)] - public static extern IntPtr MonitorFromWindow(IntPtr hwnd, MonitorFrom dwFlags); + public static extern IntPtr MonitorFromWindow(IntPtr hWnd, MonitorFrom dwFlags); [DllImport("user32", SetLastError = true)] public static extern IntPtr MonitorFromRect([In] ref RECT lprc, uint dwFlags); [DllImport("user32", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool GetWindowInfo(IntPtr hwnd, ref WindowInfo pwi); + public static extern bool GetWindowInfo(IntPtr hWnd, ref WindowInfo pwi); [DllImport("user32", SetLastError = true)] public static extern int EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam); [DllImport("user32", SetLastError = true)] public static extern int EnumChildWindows(IntPtr hWndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam); [DllImport("user32", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool GetScrollInfo(IntPtr hwnd, int fnBar, ref SCROLLINFO lpsi); + public static extern bool GetScrollInfo(IntPtr hWnd, int fnBar, ref SCROLLINFO lpsi); [DllImport("user32", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool ShowScrollBar(IntPtr hwnd, ScrollBarDirection scrollBar, [MarshalAs(UnmanagedType.Bool)] bool show); + public static extern bool ShowScrollBar(IntPtr hWnd, ScrollBarDirection scrollBar, [MarshalAs(UnmanagedType.Bool)] bool show); [DllImport("user32", SetLastError = true)] public static extern int SetScrollPos(IntPtr hWnd, Orientation nBar, int nPos, [MarshalAs(UnmanagedType.Bool)] bool bRedraw); [DllImport("user32", SetLastError = true)] @@ -157,10 +157,10 @@ namespace GreenshotPlugin.UnmanagedHelpers { public static extern IntPtr GetTopWindow(IntPtr hWnd); [DllImport("user32", SetLastError = true)] - public static extern IntPtr GetDC(IntPtr hwnd); + public static extern IntPtr GetDC(IntPtr hWnd); [DllImport("user32", SetLastError = true)] - public static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC); + public static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDc); [DllImport("user32", SetLastError = true)] public static extern IntPtr GetClipboardOwner();