public ComposedScreenshot(IntPtr targetWindow, ScreenshotMethod method = ScreenshotMethod.DWM, bool withSolidGlass = true)
            : base(targetWindow, method, withSolidGlass)
        {
            Trace.WriteLine("Creating composed screenshot...", string.Format("ComposedScreenshot.ctor [{0}]", System.Threading.Thread.CurrentThread.Name));

            CursorLocation = Cursor.Position;
            CursorImage = Core.GetCursor();

            //#32768 is the Windows Menu Class
            // || wc.StartsWith("WindowsForms10.Window.20808");
            var ContextMenus = Windowing.GetChildWindows(Windowing.GetDesktopWindow()).Where(h => Windowing.GetWindowClass(h) == "#32768").ToList();

            if (ContextMenus.Any(h => CaptureRect.IntersectsWith(Helper.GetWindowRectangle(h))))
            {
                Trace.WriteLine(string.Format("Found Win32 {0} menus...", ContextMenus.Count), string.Format("ComposedScreenshot.ctor [{0}]", System.Threading.Thread.CurrentThread.Name));

                CompositionStack.AddRange(ContextMenus.Select(h => Screenshot.FromBitmapRect(Core.ScreenshotWindow(h), Helper.GetWindowRectangle((h)))));
            }
            else
            {
                //If there are no Win32 popup context menus found, but the mouse is over something who's root is the target rect, walk that tree for composition items

                //Check if the thing the mouse is over is different (eg, an overhanging menu)
                IntPtr MouseTargetHandle = Windowing.WindowFromPoint(Cursor.Position);

                //We don't want to do this when we're trying to get popup context menus since they are children of the desktop window,
                //not the target window. However that situation is handled separately above.
                //What we're doing here is looking for overlapping windows, like GTK context menus
                if (Windowing.GetAncestor(MouseTargetHandle, Windowing.GA_ROOTOWNER) == this.TargetHandle) // && !this.TargetRect.Contains(Cursor.Position))
                {
                    Trace.WriteLine(string.Format("Mouse over something else, handle {0}...", MouseTargetHandle), string.Format("ComposedScreenshot.ctor [{0}]", System.Threading.Thread.CurrentThread.Name));

                    while (MouseTargetHandle != TargetHandle && MouseTargetHandle != IntPtr.Zero)
                    {
                        //var MouseTargetWindowRect = Helper.GetWindowRectangle(MouseTargetHandle);
                        //var MouseTargetScreenshot = Core.ScreenshotArea(MouseTargetHandle, MouseTargetWindowRect);

                        //GTK menus seem to have virtually identical parents of themselves, so I initially tried to circumvent this
                        //with a check to prevent any two same rects from being added
                        //though, it's obsolete with the more robust removal below

                        //if (!CompositionStack.Any(ss => ss.TargetRect == MouseTargetWindowRect))
                        //var layer = Screenshot.FromBitmapRect(MouseTargetScreenshot, MouseTargetWindowRect);
                        var layer = new Screenshot(MouseTargetHandle, ScreenshotMethod.GDI, false);
                        CompositionStack.Add(layer);

                        Trace.WriteLine("Added " + layer.TargetHandle.ToString() + "; Class: " + layer.WindowClass, "ComposedScreenshot.ctor");

                        MouseTargetHandle = Windowing.GetParent(MouseTargetHandle);
                    }

                    Trace.WriteLine(string.Format("Added {0} items...", CompositionStack.Count), string.Format("ComposedScreenshot.ctor [{0}]", System.Threading.Thread.CurrentThread.Name));

                    //Remove any rect which is completely inside of another. This is mainly an effort to avoid extra layers,
                    //such as those generated by GTK menus, where some elements appear to be duplicated.
                    var toRemove = CompositionStack.Where(cs => CompositionStack.Any(a => cs != a && a.TargetRect.Contains(cs.TargetRect))).ToList();

                    Trace.WriteLine(string.Format("Removing {0} items...", toRemove.Count), string.Format("ComposedScreenshot.ctor [{0}]", System.Threading.Thread.CurrentThread.Name));

                    //In a case where there's one menu and two identical rects, don't remove both of them, or we'll have nothing to compose
                    if (toRemove.Count == CompositionStack.Count && toRemove.Count > 0)
                        toRemove.RemoveAt(toRemove.Count - 1);

                    CompositionStack.RemoveAll(r => toRemove.Contains(r));

                    //If they're all fully inside the window bounds, remove them- probably just regular window elements
                    if (CompositionStack.All(cs => this.TargetRect.Contains(cs.TargetRect)))
                    {
                        Trace.WriteLine(string.Format("Clearing {0} items...", toRemove.Count), string.Format("ComposedScreenshot.ctor [{0}]", System.Threading.Thread.CurrentThread.Name));
                        //CompositionStack.RemoveAll(cs => !cs.WindowClass.StartsWith("WindowsForms10.Window.20808"));
                        CompositionStack.Clear();
                    }
                }
            }

            //Since we compose where first is bottom and last is top, we'll need to reverse what we have so far since we enumerate the other direction
            CompositionStack.Reverse();

            //Base window is always the bottom layer of the composition stack
            CompositionStack.Insert(0, this);

            Trace.WriteLine("Done.", string.Format("ComposedScreenshot.ctor [{0}]", System.Threading.Thread.CurrentThread.Name));
        }
Exemple #2
0
        private void LoadDesktopThumb()
        {
            Screenshot TaskBar = new Screenshot(Windowing.FindWindow("Shell_TrayWnd", null), ScreenshotMethod.DWM, false);
            string DesktopWallpaperPath = Registry.CurrentUser.OpenSubKey(@"Control Panel\Desktop").GetValue("Wallpaper") as string;

            Bitmap CompositedDesktop = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
            using (Graphics composite = Graphics.FromImage(CompositedDesktop))
            {
                if (string.IsNullOrWhiteSpace(DesktopWallpaperPath))
                {
                    string[] BGColor = (Registry.CurrentUser.OpenSubKey(@"Control Panel\Colors").GetValue("Background", string.Empty) as string).Split(' ');
                    if (BGColor.Length >= 3)
                    {
                        int r, g, b;
                        if (int.TryParse(BGColor[0], out r) && int.TryParse(BGColor[1], out g) && int.TryParse(BGColor[2], out b))
                            composite.FillRectangle(new SolidBrush(Color.FromArgb(r, g, b)), 0, 0, CompositedDesktop.Width, CompositedDesktop.Height);
                    }
                }
                else
                {
                    Image DesktopWallpaper = Image.FromFile(DesktopWallpaperPath);
                    composite.DrawImage(DesktopWallpaper, new Rectangle(0, 0, CompositedDesktop.Width, CompositedDesktop.Height), new Rectangle(0, 0, DesktopWallpaper.Width, DesktopWallpaper.Height), GraphicsUnit.Pixel);
                }

                switch (FMUtils.WinApi.Helper.GetTaskbarEdge())
                {
                    case DockStyle.Top:
                    case DockStyle.Left: composite.DrawImageUnscaled(TaskBar.BaseScreenshotImage, 0, 0); break;
                    case DockStyle.Bottom: composite.DrawImageUnscaled(TaskBar.BaseScreenshotImage, 0, Screen.PrimaryScreen.Bounds.Height - TaskBar.BaseScreenshotImage.Height); break;
                    case DockStyle.Right: composite.DrawImageUnscaled(TaskBar.BaseScreenshotImage, Screen.PrimaryScreen.Bounds.Width - TaskBar.BaseScreenshotImage.Width, 0); break;
                }

                composite.Save();
            }

            pbDesktopPreview.Image = CompositedDesktop;
            pnPreviewLocationChooser.Width = (int)((float)pbDesktopPreview.Height * ((float)CompositedDesktop.Width / (float)CompositedDesktop.Height)) + 1;
            pnPreviewLocationChooser.Left = (int)((float)this.Width / 2.0 - (float)pnPreviewLocationChooser.Width / 2.0);
        }
        public ComposedScreenshot(IntPtr targetWindow, ScreenshotMethod method = ScreenshotMethod.DWM, bool withSolidGlass = true)
            : base(targetWindow, method, withSolidGlass)
        {
            Trace.WriteLine("Creating composed screenshot...", string.Format("ComposedScreenshot.ctor [{0}]", System.Threading.Thread.CurrentThread.Name));

            CursorLocation = Cursor.Position;
            CursorImage    = Core.GetCursor();

            //#32768 is the Windows Menu Class
            // || wc.StartsWith("WindowsForms10.Window.20808");
            var ContextMenus = Windowing.GetChildWindows(Windowing.GetDesktopWindow()).Where(h => Windowing.GetWindowClass(h) == "#32768").ToList();

            if (ContextMenus.Any(h => CaptureRect.IntersectsWith(Helper.GetWindowRectangle(h))))
            {
                Trace.WriteLine(string.Format("Found Win32 {0} menus...", ContextMenus.Count), string.Format("ComposedScreenshot.ctor [{0}]", System.Threading.Thread.CurrentThread.Name));

                CompositionStack.AddRange(ContextMenus.Select(h => Screenshot.FromBitmapRect(Core.ScreenshotWindow(h), Helper.GetWindowRectangle((h)))));
            }
            else
            {
                //If there are no Win32 popup context menus found, but the mouse is over something who's root is the target rect, walk that tree for composition items

                //Check if the thing the mouse is over is different (eg, an overhanging menu)
                IntPtr MouseTargetHandle = Windowing.WindowFromPoint(Cursor.Position);

                //We don't want to do this when we're trying to get popup context menus since they are children of the desktop window,
                //not the target window. However that situation is handled separately above.
                //What we're doing here is looking for overlapping windows, like GTK context menus
                if (Windowing.GetAncestor(MouseTargetHandle, Windowing.GA_ROOTOWNER) == this.TargetHandle) // && !this.TargetRect.Contains(Cursor.Position))
                {
                    Trace.WriteLine(string.Format("Mouse over something else, handle {0}...", MouseTargetHandle), string.Format("ComposedScreenshot.ctor [{0}]", System.Threading.Thread.CurrentThread.Name));

                    while (MouseTargetHandle != TargetHandle && MouseTargetHandle != IntPtr.Zero)
                    {
                        //var MouseTargetWindowRect = Helper.GetWindowRectangle(MouseTargetHandle);
                        //var MouseTargetScreenshot = Core.ScreenshotArea(MouseTargetHandle, MouseTargetWindowRect);

                        //GTK menus seem to have virtually identical parents of themselves, so I initially tried to circumvent this
                        //with a check to prevent any two same rects from being added
                        //though, it's obsolete with the more robust removal below

                        //if (!CompositionStack.Any(ss => ss.TargetRect == MouseTargetWindowRect))
                        //var layer = Screenshot.FromBitmapRect(MouseTargetScreenshot, MouseTargetWindowRect);
                        var layer = new Screenshot(MouseTargetHandle, ScreenshotMethod.GDI, false);
                        CompositionStack.Add(layer);

                        Trace.WriteLine("Added " + layer.TargetHandle.ToString() + "; Class: " + layer.WindowClass, "ComposedScreenshot.ctor");

                        MouseTargetHandle = Windowing.GetParent(MouseTargetHandle);
                    }

                    Trace.WriteLine(string.Format("Added {0} items...", CompositionStack.Count), string.Format("ComposedScreenshot.ctor [{0}]", System.Threading.Thread.CurrentThread.Name));

                    //Remove any rect which is completely inside of another. This is mainly an effort to avoid extra layers,
                    //such as those generated by GTK menus, where some elements appear to be duplicated.
                    var toRemove = CompositionStack.Where(cs => CompositionStack.Any(a => cs != a && a.TargetRect.Contains(cs.TargetRect))).ToList();

                    Trace.WriteLine(string.Format("Removing {0} items...", toRemove.Count), string.Format("ComposedScreenshot.ctor [{0}]", System.Threading.Thread.CurrentThread.Name));

                    //In a case where there's one menu and two identical rects, don't remove both of them, or we'll have nothing to compose
                    if (toRemove.Count == CompositionStack.Count && toRemove.Count > 0)
                    {
                        toRemove.RemoveAt(toRemove.Count - 1);
                    }

                    CompositionStack.RemoveAll(r => toRemove.Contains(r));

                    //If they're all fully inside the window bounds, remove them- probably just regular window elements
                    if (CompositionStack.All(cs => this.TargetRect.Contains(cs.TargetRect)))
                    {
                        Trace.WriteLine(string.Format("Clearing {0} items...", toRemove.Count), string.Format("ComposedScreenshot.ctor [{0}]", System.Threading.Thread.CurrentThread.Name));
                        //CompositionStack.RemoveAll(cs => !cs.WindowClass.StartsWith("WindowsForms10.Window.20808"));
                        CompositionStack.Clear();
                    }
                }
            }

            //Since we compose where first is bottom and last is top, we'll need to reverse what we have so far since we enumerate the other direction
            CompositionStack.Reverse();

            //Base window is always the bottom layer of the composition stack
            CompositionStack.Insert(0, this);

            Trace.WriteLine("Done.", string.Format("ComposedScreenshot.ctor [{0}]", System.Threading.Thread.CurrentThread.Name));
        }