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();