/// <summary> /// Capture the supplied Window /// </summary> /// <param name="windowToCapture">Window to capture</param> /// <param name="captureForWindow">The capture to store the details</param> /// <param name="windowCaptureMode">What WindowCaptureMode to use</param> /// <returns></returns> public static ICapture CaptureWindow(WindowDetails windowToCapture, ICapture captureForWindow, WindowCaptureMode windowCaptureMode) { if (captureForWindow == null) { captureForWindow = new Capture(); } Rectangle windowRectangle = windowToCapture.WindowRectangle; // When Vista & DWM (Aero) enabled bool dwmEnabled = DWM.IsDwmEnabled(); // get process name to be able to exclude certain processes from certain capture modes using (Process process = windowToCapture.Process) { bool isAutoMode = windowCaptureMode == WindowCaptureMode.Auto; // For WindowCaptureMode.Auto we check: // 1) Is window IE, use IE Capture // 2) Is Windows >= Vista & DWM enabled: use DWM // 3) Otherwise use GDI (Screen might be also okay but might lose content) if (isAutoMode) { if (CoreConfig.IECapture && IeCaptureHelper.IsIeWindow(windowToCapture)) { try { ICapture ieCapture = IeCaptureHelper.CaptureIe(captureForWindow, windowToCapture); if (ieCapture != null) { return(ieCapture); } } catch (Exception ex) { Log.WarnFormat("Problem capturing IE, skipping to normal capture. Exception message was: {0}", ex.Message); } } // Take default screen windowCaptureMode = WindowCaptureMode.Screen; // Change to GDI, if allowed if (!windowToCapture.IsMetroApp && WindowCapture.IsGdiAllowed(process)) { if (!dwmEnabled && IsWpf(process)) { // do not use GDI, as DWM is not enabled and the application uses PresentationFramework.dll -> isWPF Log.InfoFormat("Not using GDI for windows of process {0}, as the process uses WPF", process.ProcessName); } else { windowCaptureMode = WindowCaptureMode.GDI; } } // Change to DWM, if enabled and allowed if (dwmEnabled) { if (windowToCapture.IsMetroApp || WindowCapture.IsDwmAllowed(process)) { windowCaptureMode = WindowCaptureMode.Aero; } } } else if (windowCaptureMode == WindowCaptureMode.Aero || windowCaptureMode == WindowCaptureMode.AeroTransparent) { if (!dwmEnabled || (!windowToCapture.IsMetroApp && !WindowCapture.IsDwmAllowed(process))) { // Take default screen windowCaptureMode = WindowCaptureMode.Screen; // Change to GDI, if allowed if (WindowCapture.IsGdiAllowed(process)) { windowCaptureMode = WindowCaptureMode.GDI; } } } else if (windowCaptureMode == WindowCaptureMode.GDI && !WindowCapture.IsGdiAllowed(process)) { // GDI not allowed, take screen windowCaptureMode = WindowCaptureMode.Screen; } Log.InfoFormat("Capturing window with mode {0}", windowCaptureMode); bool captureTaken = false; windowRectangle.Intersect(captureForWindow.ScreenBounds); // Try to capture while (!captureTaken) { ICapture tmpCapture = null; switch (windowCaptureMode) { case WindowCaptureMode.GDI: if (WindowCapture.IsGdiAllowed(process)) { if (windowToCapture.Iconic) { // Restore the window making sure it's visible! windowToCapture.Restore(); } else { windowToCapture.ToForeground(false); } tmpCapture = windowToCapture.CaptureGdiWindow(captureForWindow); if (tmpCapture != null) { // check if GDI capture any good, by comparing it with the screen content int blackCountGdi = ImageHelper.CountColor(tmpCapture.Image, Color.Black, false); int gdiPixels = tmpCapture.Image.Width * tmpCapture.Image.Height; int blackPercentageGdi = blackCountGdi * 100 / gdiPixels; if (blackPercentageGdi >= 1) { int screenPixels = windowRectangle.Width * windowRectangle.Height; using (ICapture screenCapture = new Capture()) { screenCapture.CaptureDetails = captureForWindow.CaptureDetails; if (WindowCapture.CaptureRectangleFromDesktopScreen(screenCapture, windowRectangle) != null) { int blackCountScreen = ImageHelper.CountColor(screenCapture.Image, Color.Black, false); int blackPercentageScreen = blackCountScreen * 100 / screenPixels; if (screenPixels == gdiPixels) { // "easy compare", both have the same size // If GDI has more black, use the screen capture. if (blackPercentageGdi > blackPercentageScreen) { Log.Debug("Using screen capture, as GDI had additional black."); // changeing the image will automatically dispose the previous tmpCapture.Image = screenCapture.Image; // Make sure it's not disposed, else the picture is gone! screenCapture.NullImage(); } } else if (screenPixels < gdiPixels) { // Screen capture is cropped, window is outside of screen if (blackPercentageGdi > 50 && blackPercentageGdi > blackPercentageScreen) { Log.Debug("Using screen capture, as GDI had additional black."); // changeing the image will automatically dispose the previous tmpCapture.Image = screenCapture.Image; // Make sure it's not disposed, else the picture is gone! screenCapture.NullImage(); } } else { // Use the GDI capture by doing nothing Log.Debug("This should not happen, how can there be more screen as GDI pixels?"); } } } } } } if (tmpCapture != null) { captureForWindow = tmpCapture; captureTaken = true; } else { // A problem, try Screen windowCaptureMode = WindowCaptureMode.Screen; } break; case WindowCaptureMode.Aero: case WindowCaptureMode.AeroTransparent: if (windowToCapture.IsMetroApp || WindowCapture.IsDwmAllowed(process)) { tmpCapture = windowToCapture.CaptureDwmWindow(captureForWindow, windowCaptureMode, isAutoMode); } if (tmpCapture != null) { captureForWindow = tmpCapture; captureTaken = true; } else { // A problem, try GDI windowCaptureMode = WindowCaptureMode.GDI; } break; default: // Screen capture if (windowToCapture.Iconic) { // Restore the window making sure it's visible! windowToCapture.Restore(); } else { windowToCapture.ToForeground(); } try { captureForWindow = WindowCapture.CaptureRectangleFromDesktopScreen(captureForWindow, windowRectangle); captureTaken = true; } catch (Exception e) { Log.Error("Problem capturing", e); return(null); } break; } } } if (captureForWindow != null) { captureForWindow.CaptureDetails.Title = windowToCapture.Text; } return(captureForWindow); }
/// <summary> /// Make Capture with specified destinations /// </summary> private void MakeCapture() { Thread retrieveWindowDetailsThread = null; // This fixes a problem when a balloon is still visible and a capture needs to be taken // forcefully removes the balloon! if (!CoreConfig.HideTrayicon) { MainForm.Instance.NotifyIcon.Visible = false; MainForm.Instance.NotifyIcon.Visible = true; } Log.Debug($"Capturing with mode {_captureMode} and using Cursor {_captureMouseCursor}"); _capture.CaptureDetails.CaptureMode = _captureMode; // Get the windows details in a seperate thread, only for those captures that have a Feedback // As currently the "elements" aren't used, we don't need them yet switch (_captureMode) { case CaptureMode.Region: // Check if a region is pre-supplied! if (Rectangle.Empty.Equals(_captureRect)) { retrieveWindowDetailsThread = PrepareForCaptureWithFeedback(); } break; case CaptureMode.Window: retrieveWindowDetailsThread = PrepareForCaptureWithFeedback(); break; } // Add destinations if no-one passed a handler if (_capture.CaptureDetails.CaptureDestinations == null || _capture.CaptureDetails.CaptureDestinations.Count == 0) { AddConfiguredDestination(); } // Delay for the Context menu if (CoreConfig.CaptureDelay > 0) { Thread.Sleep(CoreConfig.CaptureDelay); } else { CoreConfig.CaptureDelay = 0; } // Capture Mousecursor if we are not loading from file or clipboard, only show when needed if (_captureMode != CaptureMode.File && _captureMode != CaptureMode.Clipboard) { _capture = WindowCapture.CaptureCursor(_capture); _capture.CursorVisible = _captureMouseCursor && CoreConfig.CaptureMousepointer; } switch (_captureMode) { case CaptureMode.Window: _capture = WindowCapture.CaptureScreen(_capture); _capture.CaptureDetails.AddMetaData("source", "Screen"); SetDpi(); CaptureWithFeedback(); break; case CaptureMode.ActiveWindow: if (CaptureActiveWindow()) { // Capture worked, offset mouse according to screen bounds and capture location _capture.MoveMouseLocation(_capture.ScreenBounds.Location.X - _capture.Location.X, _capture.ScreenBounds.Location.Y - _capture.Location.Y); _capture.CaptureDetails.AddMetaData("source", "Window"); } else { _captureMode = CaptureMode.FullScreen; _capture = WindowCapture.CaptureScreen(_capture); _capture.CaptureDetails.AddMetaData("source", "Screen"); _capture.CaptureDetails.Title = "Screen"; } SetDpi(); HandleCapture(); break; case CaptureMode.IE: if (IeCaptureHelper.CaptureIe(_capture, SelectedCaptureWindow) != null) { _capture.CaptureDetails.AddMetaData("source", "Internet Explorer"); SetDpi(); HandleCapture(); } break; case CaptureMode.FullScreen: // Check how we need to capture the screen bool captureTaken = false; switch (_screenCaptureMode) { case ScreenCaptureMode.Auto: Point mouseLocation = User32.GetCursorLocation(); foreach (Screen screen in Screen.AllScreens) { if (screen.Bounds.Contains(mouseLocation)) { _capture = WindowCapture.CaptureRectangle(_capture, screen.Bounds); captureTaken = true; break; } } break; case ScreenCaptureMode.Fixed: if (CoreConfig.ScreenToCapture > 0 && CoreConfig.ScreenToCapture <= Screen.AllScreens.Length) { _capture = WindowCapture.CaptureRectangle(_capture, Screen.AllScreens[CoreConfig.ScreenToCapture].Bounds); captureTaken = true; } break; case ScreenCaptureMode.FullScreen: // Do nothing, we take the fullscreen capture automatically break; } if (!captureTaken) { _capture = WindowCapture.CaptureScreen(_capture); } SetDpi(); HandleCapture(); break; case CaptureMode.Clipboard: Image clipboardImage = ClipboardHelper.GetImage(); if (clipboardImage != null) { if (_capture != null) { _capture.Image = clipboardImage; } else { _capture = new Capture(clipboardImage); } _capture.CaptureDetails.Title = "Clipboard"; _capture.CaptureDetails.AddMetaData("source", "Clipboard"); // Force Editor, keep picker if (_capture.CaptureDetails.HasDestination(PickerDestination.DESIGNATION)) { _capture.CaptureDetails.ClearDestinations(); _capture.CaptureDetails.AddDestination(DestinationHelper.GetDestination(EditorDestination.DESIGNATION)); _capture.CaptureDetails.AddDestination(DestinationHelper.GetDestination(PickerDestination.DESIGNATION)); } else { _capture.CaptureDetails.ClearDestinations(); _capture.CaptureDetails.AddDestination(DestinationHelper.GetDestination(EditorDestination.DESIGNATION)); } HandleCapture(); } else { MessageBox.Show(Language.GetString("clipboard_noimage")); } break; case CaptureMode.File: Image fileImage = null; string filename = _capture.CaptureDetails.Filename; if (!string.IsNullOrEmpty(filename)) { try { if (filename.ToLower().EndsWith("." + OutputFormat.greenshot)) { ISurface surface = new Surface(); surface = ImageOutput.LoadGreenshotSurface(filename, surface); surface.CaptureDetails = _capture.CaptureDetails; DestinationHelper.GetDestination(EditorDestination.DESIGNATION).ExportCapture(true, surface, _capture.CaptureDetails); break; } } catch (Exception e) { Log.Error(e.Message, e); MessageBox.Show(Language.GetFormattedString(LangKey.error_openfile, filename)); } try { fileImage = ImageHelper.LoadImage(filename); } catch (Exception e) { Log.Error(e.Message, e); MessageBox.Show(Language.GetFormattedString(LangKey.error_openfile, filename)); } } if (fileImage != null) { _capture.CaptureDetails.Title = Path.GetFileNameWithoutExtension(filename); _capture.CaptureDetails.AddMetaData("file", filename); _capture.CaptureDetails.AddMetaData("source", "file"); if (_capture != null) { _capture.Image = fileImage; } else { _capture = new Capture(fileImage); } // Force Editor, keep picker, this is currently the only usefull destination if (_capture.CaptureDetails.HasDestination(PickerDestination.DESIGNATION)) { _capture.CaptureDetails.ClearDestinations(); _capture.CaptureDetails.AddDestination(DestinationHelper.GetDestination(EditorDestination.DESIGNATION)); _capture.CaptureDetails.AddDestination(DestinationHelper.GetDestination(PickerDestination.DESIGNATION)); } else { _capture.CaptureDetails.ClearDestinations(); _capture.CaptureDetails.AddDestination(DestinationHelper.GetDestination(EditorDestination.DESIGNATION)); } HandleCapture(); } break; case CaptureMode.LastRegion: if (!CoreConfig.LastCapturedRegion.IsEmpty) { _capture = WindowCapture.CaptureRectangle(_capture, CoreConfig.LastCapturedRegion); // TODO: Reactive / check if the elements code is activated //if (windowDetailsThread != null) { // windowDetailsThread.Join(); //} // Set capture title, fixing bug #3569703 foreach (WindowDetails window in WindowDetails.GetVisibleWindows()) { Point estimatedLocation = new Point(CoreConfig.LastCapturedRegion.X + CoreConfig.LastCapturedRegion.Width / 2, CoreConfig.LastCapturedRegion.Y + CoreConfig.LastCapturedRegion.Height / 2); if (window.Contains(estimatedLocation)) { _selectedCaptureWindow = window; _capture.CaptureDetails.Title = _selectedCaptureWindow.Text; break; } } // Move cursor, fixing bug #3569703 _capture.MoveMouseLocation(_capture.ScreenBounds.Location.X - _capture.Location.X, _capture.ScreenBounds.Location.Y - _capture.Location.Y); //capture.MoveElements(capture.ScreenBounds.Location.X - capture.Location.X, capture.ScreenBounds.Location.Y - capture.Location.Y); _capture.CaptureDetails.AddMetaData("source", "screen"); SetDpi(); HandleCapture(); } break; case CaptureMode.Region: // Check if a region is pre-supplied! if (Rectangle.Empty.Equals(_captureRect)) { _capture = WindowCapture.CaptureScreen(_capture); _capture.CaptureDetails.AddMetaData("source", "screen"); SetDpi(); CaptureWithFeedback(); } else { _capture = WindowCapture.CaptureRectangle(_capture, _captureRect); _capture.CaptureDetails.AddMetaData("source", "screen"); SetDpi(); HandleCapture(); } break; default: Log.Warn("Unknown capture mode: " + _captureMode); break; } // Wait for thread, otherwise we can't dipose the CaptureHelper retrieveWindowDetailsThread?.Join(); if (_capture != null) { Log.Debug("Disposing capture"); _capture.Dispose(); } }