/* * Greenshot - a free and open source screenshot tool * Copyright (C) 2007-2012 Thomas Braun, Jens Klingen, Robin Krom * * For more information see: http://getgreenshot.org/ * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/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.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Runtime.InteropServices; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Windows.Forms; using Greenshot.IniFile; using Greenshot.Plugin; using GreenshotPlugin.UnmanagedHelpers; /// /// 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. /// namespace GreenshotPlugin.Core { #region EnumWindows /// /// EnumWindows wrapper for .NET /// public class WindowsEnumerator { #region Member Variables private List items = null; #endregion /// /// Returns the collection of windows returned by /// GetWindows /// public List Items { get { return this.items; } } /// /// 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) { if (parent != null) { GetWindows(parent.Handle, null); } else { GetWindows(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) { this.items = new List(); List windows = new List(); User32.EnumChildWindows(hWndParent, new EnumWindowsProc(this.WindowEnum), 0); bool hasParent = !IntPtr.Zero.Equals(hWndParent); string parentText = null; if (hasParent) { StringBuilder title = new StringBuilder(260, 260); User32.GetWindowText(hWndParent, title, title.Capacity); parentText = title.ToString(); } foreach (WindowDetails 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; } #region EnumWindows callback /// /// The enum Windows callback. /// /// Window Handle /// Application defined value /// 1 to continue enumeration, 0 to stop private int WindowEnum(IntPtr hWnd, int lParam) { if (this.OnWindowEnum(hWnd)) { return 1; } else { return 0; } } #endregion /// /// 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; } #region Constructor, Dispose public WindowsEnumerator() { // nothing to do } #endregion } #endregion EnumWindows /// #region WindowDetails /// /// Provides details about a Window returned by the /// enumeration /// public class WindowDetails : IEquatable{ private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(WindowDetails)); private static Dictionary> classnameTree = new Dictionary>(); private static CoreConfiguration conf = IniConfig.GetIniSection(); private static List ignoreHandles = new List(); private static Dictionary iconCache = new Dictionary(); private static List excludeProcessesFromFreeze = new List(); public static void AddProcessToExcludeFromFreeze(string processname) { if (!excludeProcessesFromFreeze.Contains(processname)) { excludeProcessesFromFreeze.Add(processname); } } internal static bool isIgnoreHandle(IntPtr handle) { return ignoreHandles.Contains(handle); } private List childWindows = null; private IntPtr parentHandle = IntPtr.Zero; private WindowDetails parent = null; private bool frozen = false; /// /// The window handle. /// private IntPtr hWnd = IntPtr.Zero; /// /// 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 this.Equals(right as WindowDetails); } public bool Equals(WindowDetails other) { if (Object.ReferenceEquals(other, null)) { return false; } if (Object.ReferenceEquals(this, other)) { return true; } if (this.GetType() != other.GetType()){ return false; } return other.Handle == Handle; } public bool HasChildren { get { return (childWindows != null) && (childWindows.Count > 0); } } public void FreezeDetails() { frozen = true; } public void UnfreezeDetails() { frozen = false; } public string ProcessPath { get { if (Handle == IntPtr.Zero) { // not a valid window handle return string.Empty; } // Get the process id IntPtr processid; User32.GetWindowThreadProcessId(Handle, out processid); return Kernel32.GetProcessPath(processid); } } /// /// Get the icon belonging to the process /// public Image DisplayIcon { get { try { string filename = ProcessPath; if (!iconCache.ContainsKey(filename)) { Image icon = null; if (File.Exists(filename)) { using (Icon appIcon = Icon.ExtractAssociatedIcon(filename)) { if (appIcon != null) { icon = appIcon.ToBitmap(); } } } iconCache.Add(filename, icon); } return iconCache[filename]; } catch (Exception ex) { LOG.WarnFormat("Couldn't get icon for window {0} due to: {1}", Text, ex.Message); LOG.Warn(ex); } return null; } } /// /// 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 List 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 with all the found windows private static List FindWindow(List windows, string titlePattern, string classnamePattern) { List foundWindows = new List(); 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)) { foundWindows.Add(window); } else if (classnameRegexp != null && classnameRegexp.IsMatch(window.ClassName)) { foundWindows.Add(window); } } return foundWindows; } /// /// Retrieve the child with mathing classname /// public WindowDetails GetChild(string childClassname) { foreach(WindowDetails child in Children) { if (childClassname.Equals(child.ClassName)) { return child; } } return null; } /// /// Retrieve the children with mathing 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 List 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 List GetChildren(int levelsToGo) { if (childWindows == null) { childWindows = new List(); foreach(WindowDetails childWindow in new WindowsEnumerator().GetWindows(hWnd, null).Items) { childWindows.Add(childWindow); 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 with all the found windows, or an emptry list public List FindChildren(string titlePattern, string classnamePattern) { return FindWindow(Children, titlePattern, classnamePattern); } /// /// Recursing helper method for the FindPath /// /// List with classnames /// The index in the list to look for /// WindowDetails if a match was found private WindowDetails FindPath(List classnames, int index) { WindowDetails resultWindow = null; List foundWindows = FindChildren(null, classnames[index]); if (index == classnames.Count - 1) { if (foundWindows.Count > 0) { resultWindow = foundWindows[0]; } } else { foreach(WindowDetails foundWindow in foundWindows) { resultWindow = foundWindow.FindPath(classnames, index+1); if (resultWindow != null) { break; } } } return resultWindow; } /// /// 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 with classname "path" /// true allows the search to skip a classname of the path /// WindowDetails if found public WindowDetails FindPath(List classnames, bool allowSkip) { int index = 0; WindowDetails 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 /// /// 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(WindowDetails child in windowDetails.Children) { if (classnamePattern.IsMatch(child.ClassName)) { return child; } } // Go into all children foreach(WindowDetails child in windowDetails.Children) { WindowDetails 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) { IntPtr tmphWnd = User32.GetWindow(Handle, gwCommand); if (IntPtr.Zero == tmphWnd) { return null; } WindowDetails windowDetails = new WindowDetails(tmphWnd); windowDetails.parent = this; return windowDetails; } /// /// Gets the window's handle /// public IntPtr Handle { get { return this.hWnd; } } private string text = null; /// /// Gets the window's title (caption) /// public string Text { set { text = value; } get { if (text == null) { StringBuilder title = new StringBuilder(260, 260); User32.GetWindowText(this.hWnd, title, title.Capacity); text = title.ToString(); } return text; } } private string className = null; /// /// Gets the window's class name. /// public string ClassName { get { if (className == null) { StringBuilder classNameBuilder = new StringBuilder(260, 260); User32.GetClassName(this.hWnd, classNameBuilder, classNameBuilder.Capacity); className = classNameBuilder.ToString(); } return className; } } /// /// Gets/Sets whether the window is iconic (mimimised) or not. /// public bool Iconic { get { return User32.IsIconic(this.hWnd) || Location.X <= -32000; } set { User32.SendMessage( this.hWnd, User32.WM_SYSCOMMAND, (IntPtr)User32.SC_MINIMIZE, IntPtr.Zero); } } /// /// Gets/Sets whether the window is maximised or not. /// public bool Maximised { get { return User32.IsZoomed(this.hWnd); } set { User32.SendMessage( this.hWnd, User32.WM_SYSCOMMAND, (IntPtr)User32.SC_MAXIMIZE, IntPtr.Zero); } } /// /// Gets whether the window is visible. /// public bool Visible { get { return User32.IsWindowVisible(this.hWnd); } } public bool HasParent { get { GetParent(); return parentHandle != IntPtr.Zero; } } public IntPtr ProcessId { get { IntPtr processId; User32.GetWindowThreadProcessId(Handle, out processId); return processId; } } public Process Process { get { try { IntPtr processId; User32.GetWindowThreadProcessId(Handle, out processId); Process process = Process.GetProcessById(processId.ToInt32()); if (process != null) { return process; } } 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 = 0; private const long CACHE_TIME = 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 > CACHE_TIME) { Rectangle windowRect = Rectangle.Empty; if (!HasParent && DWM.isDWMEnabled()) { GetExtendedFrameBounds(out windowRect); } if (windowRect.IsEmpty) { GetWindowRect(out windowRect); } if (!HasParent && this.Maximised) { Size size = Size.Empty; GetBorderSize(out 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 { Rectangle clientRect = Rectangle.Empty; GetClientRect(out clientRect); 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(Cursor.Position); } /// /// Restores and Brings the window to the front, /// assuming it is a visible application window. /// public void Restore() { if (Iconic) { User32.SendMessage(this.hWnd, User32.WM_SYSCOMMAND, (IntPtr)User32.SC_RESTORE, IntPtr.Zero); } User32.BringWindowToTop(this.hWnd); User32.SetForegroundWindow(this.hWnd); // Make sure windows has time to perform the action while(Iconic) { Application.DoEvents(); } } public WindowStyleFlags WindowStyle { get { return (WindowStyleFlags)User32.GetWindowLongWrapper(this.hWnd, (int)WindowLongIndex.GWL_STYLE); } set { User32.SetWindowLong(this.hWnd, (int)WindowLongIndex.GWL_STYLE, (uint)value); } } public WindowPlacement GetWindowPlacement() { WindowPlacement placement = WindowPlacement.Default; User32.GetWindowPlacement(this.Handle, ref placement); return placement; } public void SetWindowPlacement(WindowPlacement placement) { User32.SetWindowPlacement(this.Handle, ref placement); } public ExtendedWindowStyleFlags ExtendedWindowStyle { get { return (ExtendedWindowStyleFlags)User32.GetWindowLongWrapper(this.hWnd, (int)WindowLongIndex.GWL_EXSTYLE); } } /// /// 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(); tempForm.ShowInTaskbar = false; tempForm.FormBorderStyle = FormBorderStyle.None; tempForm.TopMost = true; // Register the Thumbnail DWM.DwmRegisterThumbnail(tempForm.Handle, Handle, out thumbnailHandle); // Get the original size SIZE sourceSize; DWM.DwmQueryThumbnailSourceSize(thumbnailHandle, out sourceSize); if (sourceSize.width <= 0 || sourceSize.height <= 0) { return null; } // Calculate the location of the temp form Point formLocation; Rectangle windowRectangle = WindowRectangle; Size borderSize = new Size(); if (!Maximised) { Screen displayScreen = null; // Find the screen where the window is and check if it fits foreach(Screen screen in Screen.AllScreens) { if (screen.WorkingArea.Contains(windowRectangle)) { displayScreen = screen; break; } } if (displayScreen == null) { // If none found we find the biggest screen foreach(Screen screen in Screen.AllScreens) { if (displayScreen == null || (screen.Bounds.Width >= displayScreen.Bounds.Width && screen.Bounds.Height >= displayScreen.Bounds.Height)) { displayScreen = screen; } } // check if the window will fit if (displayScreen != null && displayScreen.Bounds.Contains(new Rectangle(Point.Empty, windowRectangle.Size))) { formLocation = new Point(displayScreen.Bounds.X, displayScreen.Bounds.Y); } else { // No, just use the primary screen formLocation = new Point(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y); } } else { // The window actually fits, so while capturing create the copy over the original formLocation = new Point(windowRectangle.X, windowRectangle.Y); } } else { //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 captureRectangle.X += borderSize.Width; captureRectangle.Y += borderSize.Height; // and subtrackting the border from the size (2 times, as we move right/down for the capture without resizing) captureRectangle.Width -= 2 * borderSize.Width; captureRectangle.Height -= 2 * borderSize.Height; } else if (autoMode) { // check if the capture fits if (!capture.ScreenBounds.Contains(captureRectangle)) { // if GDI is allowed.. if (WindowCapture.isGDIAllowed(Process)) { // 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(); props.Opacity = (byte)255; props.Visible = true; props.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; try { this.FreezeWindow(); // Use red to make removal of the corners possible tempForm.BackColor = Color.Red; // Make sure everything is visible tempForm.Refresh(); Application.DoEvents(); using (Bitmap redMask = WindowCapture.CaptureRectangle(captureRectangle)) { // Check if we make a transparent capture if (windowCaptureMode == WindowCaptureMode.AeroTransparent) { // 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(); Application.DoEvents(); using (Bitmap blackBitmap = WindowCapture.CaptureRectangle(captureRectangle)) { capturedBitmap = ApplyTransparency(blackBitmap, whiteBitmap); } } } catch (Exception e) { LOG.Debug("Exception: ", e); // Some problem occured, 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(); Application.DoEvents(); // Capture from the screen capturedBitmap = WindowCapture.CaptureRectangle(captureRectangle); } if (capturedBitmap != null && redMask != null) { // 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; } } if (capturedBitmap != null) { if (conf.WindowCaptureRemoveCorners && !Maximised) { Color cornerColor = Color.Transparent; if (!Image.IsAlphaPixelFormat(capturedBitmap.PixelFormat)) { cornerColor = Color.FromArgb(255, conf.DWMBackgroundColor.R, conf.DWMBackgroundColor.G, conf.DWMBackgroundColor.B); } RemoveCorners(capturedBitmap, cornerColor); } } } } finally { // Make sure to ALWAYS unfreeze!! this.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(); } } return capture; } /// /// Helper method to remove the corners from a DMW capture /// /// The bitmap to remove the corners from. /// The background color private void RemoveCorners(Bitmap normalBitmap, Color cornerColor) { int borderWidth = User32.GetSystemMetrics(SystemMetric.SM_CXFRAME)+2; int borderHeight = User32.GetSystemMetrics(SystemMetric.SM_CYFRAME)+2; IntPtr regionHandle = GDI32.CreateRoundRectRgn(0, 0, normalBitmap.Width+1, normalBitmap.Height+1, borderWidth, borderHeight); Region region = Region.FromHrgn(regionHandle); GDI32.DeleteObject(regionHandle); using (Graphics graphics = Graphics.FromImage(normalBitmap)) { graphics.ExcludeClip(region); graphics.Clear(cornerColor); } } /// /// 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, exect for the colors. /// /// Bitmap with the black image /// Bitmap with the black image /// Bitmap with transparency private Bitmap ApplyTransparency(Bitmap blackBitmap, Bitmap whiteBitmap) { Bitmap returnBitmap = new Bitmap(blackBitmap.Width, blackBitmap.Height, PixelFormat.Format32bppArgb); using (BitmapBuffer blackBuffer = new BitmapBuffer(blackBitmap, false)) { blackBuffer.Lock(); using (BitmapBuffer whiteBuffer = new BitmapBuffer(whiteBitmap, false)) { whiteBuffer.Lock(); using (BitmapBuffer targetBuffer = new BitmapBuffer(returnBitmap, false)) { targetBuffer.Lock(); for(int y=0; y /// Helper method to get the window size for DWM Windows /// /// out Rectangle /// bool true if it worked private bool GetExtendedFrameBounds(out Rectangle rectangle) { RECT rect; int result = DWM.DwmGetWindowAttribute(Handle, (int)DWMWINDOWATTRIBUTE.DWMWA_EXTENDED_FRAME_BOUNDS, out 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) { WindowInfo windowInfo = new WindowInfo(); // Get the Window Info for this window bool result = User32.GetWindowInfo(Handle, ref windowInfo); if (result) { rectangle = windowInfo.rcClient.ToRectangle(); } else { rectangle = 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) { WindowInfo windowInfo = new WindowInfo(); // Get the Window Info for this window bool result = User32.GetWindowInfo(Handle, ref windowInfo); if (result) { rectangle = windowInfo.rcWindow.ToRectangle(); } else { rectangle = Rectangle.Empty; } return result; } /// /// Helper method to get the Border size for GDI Windows /// /// out Rectangle /// bool true if it worked private bool GetBorderSize(out Size size) { WindowInfo windowInfo = new WindowInfo(); // Get the Window Info for this window bool result = User32.GetWindowInfo(Handle, ref windowInfo); if (result) { size = new Size((int)windowInfo.cxWindowBorders, (int)windowInfo.cyWindowBorders); } else { size = Size.Empty; } return result; } /// /// Set the window as foreground window /// public static void ToForeground(IntPtr handle) { User32.SetForegroundWindow(handle); } /// /// Set the window as foreground window /// public void ToForeground() { ToForeground(this.Handle); } /// /// Get the region for a window /// private Region GetRegion() { IntPtr windowRegionPtr = GDI32.CreateRectRgn(0,0,0,0); RegionResult result = User32.GetWindowRgn(Handle, windowRegionPtr); Region returnRegion = null; if (result != RegionResult.REGION_ERROR && result != RegionResult.REGION_NULLREGION) { returnRegion = Region.FromHrgn(windowRegionPtr); } // Free the region object GDI32.DeleteObject(windowRegionPtr); return returnRegion; } 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 void FreezeWindow() { Process proc = Process.GetProcessById(this.ProcessId.ToInt32()); string processName = proc.ProcessName; if (!CanFreezeOrUnfreeze(processName)) { LOG.DebugFormat("Not freezing {0}", processName); return; } if (!CanFreezeOrUnfreeze(Text)) { LOG.DebugFormat("Not freezing {0}", processName); return; } 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; } Kernel32.SuspendThread(pOpenThread); } } /// /// Unfreeze the process belonging to the window /// public void UnfreezeWindow() { Process proc = Process.GetProcessById(this.ProcessId.ToInt32()); 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 representating 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 = null; 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)) { IntPtr hDCDest = graphics.GetHdc(); try { bool printSucceeded = User32.PrintWindow(Handle, hDCDest, 0x0); if (!printSucceeded) { // something went wrong, most likely a "0x80004005" (Acess Denied) when using UAC exceptionOccured = User32.CreateWin32Exception("PrintWindow"); } } finally { graphics.ReleaseHdc(hDCDest); } // 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); if (returnImage != null) { returnImage.Dispose(); } return null; } if (!HasParent && this.Maximised) { LOG.Debug("Correcting for maximalization"); Size borderSize = Size.Empty; GetBorderSize(out 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) { this.hWnd = 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 != null && hWnd != IntPtr.Zero) { if (ignoreHandles.Contains(hWnd)) { return WindowDetails.GetDesktopWindow(); } WindowDetails activeWindow = new WindowDetails(hWnd); // Invisible Windows should not be active if (!activeWindow.Visible) { return WindowDetails.GetDesktopWindow(); } return activeWindow; } return null; } /// /// Check if this window is Greenshot /// public bool IsGreenshot { get { try { return "Greenshot".Equals(Process.MainModule.FileVersionInfo.ProductName); } catch (Exception ex) { LOG.Warn(ex); } return false; } } /// /// Gets the Destop window /// /// WindowDetails for the destop window public static WindowDetails GetDesktopWindow() { return new WindowDetails(User32.GetDesktopWindow()); } /// /// Get all the top level windows /// /// List with all the top level windows public static List GetAllWindows() { return GetAllWindows(null); } /// /// Get all the top level windows, with matching classname /// /// List with all the top level windows public static List GetAllWindows(string classname) { return new WindowsEnumerator().GetWindows(IntPtr.Zero, classname).Items; } /// /// Recursive "find children which" /// /// Window to look into /// point to check for /// public WindowDetails FindChildUnderPoint(Point point) { if (!Contains(point)) { return null; } foreach(WindowDetails childWindow in Children) { if (childWindow.Contains(point)) { return childWindow.FindChildUnderPoint(point); } } return this; } /// /// Get all the visible top level windows /// /// List with all the visible top level windows public static List GetVisibleWindows() { List windows = new List(); Rectangle screenBounds = WindowCapture.GetScreenBounds(); List allWindows = WindowDetails.GetAllWindows(); foreach(WindowDetails window in allWindows) { // Ignore windows without title if (window.Text.Length == 0) { continue; } // Ignore invisible if (!window.Visible) { continue; } // Ignore some classes List ignoreClasses = new List(new string[] { "Progman", "XLMAIN", "Button", "Dwm" }); //"MS-SDIa" if (ignoreClasses.Contains(window.ClassName)) { continue; } // Windows without size Rectangle windowRect = window.WindowRectangle; windowRect.Intersect(screenBounds); if (windowRect.Size.IsEmpty) { continue; } windows.Add(window); } return windows; } /// /// Get all the top level windows /// /// List with all the top level windows public static List GetTopLevelWindows() { List windows = new List(); Rectangle screenBounds = WindowCapture.GetScreenBounds(); List allWindows = WindowDetails.GetAllWindows(); foreach (WindowDetails window in allWindows) { // Ignore windows without title if (window.Text.Length == 0) { continue; } // Ignore some classes List ignoreClasses = new List(new string[] { "Progman", "XLMAIN", "Button", "Dwm" }); //"MS-SDIa" if (ignoreClasses.Contains(window.ClassName)) { continue; } // Windows without size if (window.WindowRectangle.Size.IsEmpty) { continue; } if (window.HasParent) { continue; } if ((window.ExtendedWindowStyle & ExtendedWindowStyleFlags.WS_EX_TOOLWINDOW) != 0) { continue; } if (!window.Visible && !window.Iconic) { continue; } windows.Add(window); } return windows; } /// /// Find a window belonging to the same process as the supplied window. /// /// /// public static WindowDetails GetLinkedWindow(WindowDetails windowToLinkTo) { IntPtr processIdSelectedWindow = windowToLinkTo.ProcessId; foreach(WindowDetails window in WindowDetails.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; } /// /// Sort the list of WindowDetails according Z-Order, all not found "windows" come at the end /// /// IntPtr of the parent /// List of windows to sort /// public static List SortByZOrder(IntPtr hWndParent, List windows) { List sortedWindows = new List(); Dictionary allWindows = new Dictionary(); foreach(WindowDetails window in windows) { if (!allWindows.ContainsKey(window.Handle)) { allWindows.Add(window.Handle, window); } } // Sort by Z-Order, from top to bottom for(IntPtr hWnd = User32.GetTopWindow(hWndParent); hWnd!=IntPtr.Zero; hWnd = User32.GetWindow(hWnd, GetWindowCommand.GW_HWNDNEXT)) { if (allWindows.ContainsKey(hWnd)) { WindowDetails childWindow = allWindows[hWnd]; // Force getting the clientRectangle, used for caching Rectangle rect = childWindow.WindowRectangle; sortedWindows.Add(childWindow); // Remove so we can add the ones that were left over allWindows.Remove(hWnd); } } // Add missing children, those that didn't show up in the GetWindow enumeration if (allWindows.Count > 0) { Rectangle screenBounds = WindowCapture.GetScreenBounds(); // Copy not copied windows to the back, but only if visible foreach(IntPtr hWnd in allWindows.Keys) { WindowDetails notAddedChild = allWindows[hWnd]; Rectangle windowRect = notAddedChild.WindowRectangle; if (windowRect.Width * windowRect.Height > 0 && screenBounds.Contains(windowRect)) { sortedWindows.Add(notAddedChild); } else { LOG.DebugFormat("Skipping {0}", notAddedChild.ClassName); } } } return sortedWindows; } /// /// Helper method to "active" all windows that are not in the supplied list. /// One should preferably call "GetVisibleWindows" for the oldWindows. /// /// List with old windows public static void ActiveNewerWindows(List oldWindows) { List windowsAfter = WindowDetails.GetVisibleWindows(); foreach(WindowDetails window in windowsAfter) { if (!oldWindows.Contains(window)) { window.ToForeground(); } } } } #endregion }