예제 #1
0
        /// <summary>
        /// </summary>
        /// <returns></returns>
        public static SafeWindowDcHandle FromDesktop()
        {
            var hWndDesktop = User32Api.GetDesktopWindow();
            var hDcDesktop  = GetWindowDC(hWndDesktop);

            return(new SafeWindowDcHandle(hWndDesktop, hDcDesktop));
        }
예제 #2
0
        /// <summary>
        ///     This method will use User32 code to capture the specified captureBounds from the screen
        /// </summary>
        /// <param name="captureBounds">NativeRect with the bounds to capture</param>
        /// <returns>Bitmap which is captured from the screen at the location specified by the captureBounds</returns>
        public static Bitmap CaptureRectangle(NativeRect captureBounds)
        {
            Bitmap returnBitmap = null;

            if (captureBounds.Height <= 0 || captureBounds.Width <= 0)
            {
                Log.Warn().WriteLine("Nothing to capture, ignoring!");
                return(null);
            }
            Log.Debug().WriteLine("CaptureRectangle Called!");

            // .NET GDI+ Solution, according to some post this has a GDI+ leak...
            // See http://connect.microsoft.com/VisualStudio/feedback/details/344752/gdi-object-leak-when-calling-graphics-copyfromscreen
            // Bitmap capturedBitmap = new Bitmap(captureBounds.Width, captureBounds.Height);
            // using (Graphics graphics = Graphics.FromImage(capturedBitmap)) {
            //	graphics.CopyFromScreen(captureBounds.Location, NativePoint.Empty, captureBounds.Size, CopyPixelOperation.CaptureBlt);
            // }
            // capture.Image = capturedBitmap;
            // capture.Location = captureBounds.Location;

            using (var desktopDcHandle = SafeWindowDcHandle.FromDesktop())
            {
                if (desktopDcHandle.IsInvalid)
                {
                    // Get Exception before the error is lost
                    var exceptionToThrow = CreateCaptureException("desktopDCHandle", captureBounds);
                    // throw exception
                    throw exceptionToThrow;
                }

                // create a device context we can copy to
                using (var safeCompatibleDcHandle = Gdi32Api.CreateCompatibleDC(desktopDcHandle))
                {
                    // Check if the device context is there, if not throw an error with as much info as possible!
                    if (safeCompatibleDcHandle.IsInvalid)
                    {
                        // Get Exception before the error is lost
                        var exceptionToThrow = CreateCaptureException("CreateCompatibleDC", captureBounds);
                        // throw exception
                        throw exceptionToThrow;
                    }
                    // Create BITMAPINFOHEADER for CreateDIBSection
                    var bmi = BitmapInfoHeader.Create(captureBounds.Width, captureBounds.Height, 24);

                    // TODO: Enable when the function is available again
                    // Make sure the last error is set to 0
                    Win32.SetLastError(0);

                    // create a bitmap we can copy it to, using GetDeviceCaps to get the width/height
                    IntPtr bits0; // not used for our purposes. It returns a pointer to the raw bits that make up the bitmap.
                    // TODO: Change the usage to an enum?
                    using (var safeDibSectionHandle = Gdi32Api.CreateDIBSection(desktopDcHandle, ref bmi, 0, out bits0, IntPtr.Zero, 0))
                    {
                        if (safeDibSectionHandle.IsInvalid)
                        {
                            // Get Exception before the error is lost
                            var exceptionToThrow = CreateCaptureException("CreateDIBSection", captureBounds);
                            exceptionToThrow.Data.Add("hdcDest", safeCompatibleDcHandle.DangerousGetHandle().ToInt32());
                            exceptionToThrow.Data.Add("hdcSrc", desktopDcHandle.DangerousGetHandle().ToInt32());

                            // Throw so people can report the problem
                            throw exceptionToThrow;
                        }
                        // select the bitmap object and store the old handle
                        using (safeCompatibleDcHandle.SelectObject(safeDibSectionHandle))
                        {
                            // bitblt over (make copy)
                            // ReSharper disable once BitwiseOperatorOnEnumWithoutFlags
                            Gdi32Api.BitBlt(safeCompatibleDcHandle, 0, 0, captureBounds.Width, captureBounds.Height, desktopDcHandle, captureBounds.X, captureBounds.Y,
                                            RasterOperations.SourceCopy | RasterOperations.CaptureBlt);
                        }

                        // get a .NET image object for it
                        // A suggestion for the "A generic error occurred in GDI+." E_FAIL/0×80004005 error is to re-try...
                        var success = false;
                        ExternalException exception = null;
                        for (var i = 0; i < 3; i++)
                        {
                            try
                            {
                                // Collect all screens inside this capture
                                var screensInsideCapture = new List <Screen>();
                                foreach (var screen in Screen.AllScreens)
                                {
                                    if (screen.Bounds.IntersectsWith(captureBounds))
                                    {
                                        screensInsideCapture.Add(screen);
                                    }
                                }
                                // Check all all screens are of an equal size
                                bool offscreenContent;
                                using (var captureRegion = new Region(captureBounds))
                                {
                                    // Exclude every visible part
                                    foreach (var screen in screensInsideCapture)
                                    {
                                        captureRegion.Exclude(screen.Bounds);
                                    }
                                    // If the region is not empty, we have "offscreenContent"
                                    using (var screenGraphics = Graphics.FromHwnd(User32Api.GetDesktopWindow()))
                                    {
                                        offscreenContent = !captureRegion.IsEmpty(screenGraphics);
                                    }
                                }
                                // Check if we need to have a transparent background, needed for offscreen content
                                if (offscreenContent)
                                {
                                    using (var tmpBitmap = Image.FromHbitmap(safeDibSectionHandle.DangerousGetHandle()))
                                    {
                                        // Create a new bitmap which has a transparent background
                                        returnBitmap = BitmapFactory.CreateEmpty(tmpBitmap.Width, tmpBitmap.Height, PixelFormat.Format32bppArgb, Color.Transparent, tmpBitmap.HorizontalResolution,
                                                                                 tmpBitmap.VerticalResolution);
                                        // Content will be copied here
                                        using (var graphics = Graphics.FromImage(returnBitmap))
                                        {
                                            // For all screens copy the content to the new bitmap
                                            foreach (var screen in Screen.AllScreens)
                                            {
                                                // Make sure the bounds are offsetted to the capture bounds
                                                var screenBounds = screen.Bounds;
                                                screenBounds.Offset(-captureBounds.X, -captureBounds.Y);
                                                graphics.DrawImage(tmpBitmap, screenBounds, screenBounds.X, screenBounds.Y, screenBounds.Width, screenBounds.Height, GraphicsUnit.Pixel);
                                            }
                                        }
                                    }
                                }
                                else
                                {
                                    // All screens, which are inside the capture, are of equal size
                                    // assign image to Capture, the image will be disposed there..
                                    returnBitmap = Image.FromHbitmap(safeDibSectionHandle.DangerousGetHandle());
                                }
                                // We got through the capture without exception
                                success = true;
                                break;
                            }
                            catch (ExternalException ee)
                            {
                                Log.Warn().WriteLine(ee, "Problem getting bitmap at try " + i + " : ");
                                exception = ee;
                            }
                        }
                        if (!success)
                        {
                            Log.Error().WriteLine(null, "Still couldn't create Bitmap!");
                            if (exception != null)
                            {
                                throw exception;
                            }
                        }
                    }
                }
            }
            return(returnBitmap);
        }
예제 #3
0
        /// <summary>
        /// Start the capture
        /// </summary>
        /// <returns>Bitmap</returns>
        public Bitmap Capture()
        {
            if (_windowScroller.NeedsFocus())
            {
                User32Api.SetForegroundWindow(_windowScroller.ScrollBarWindow.Handle);
                Application.DoEvents();
                Thread.Sleep(Delay);
                Application.DoEvents();
            }

            // Find the area which is scrolling

            // 1. Take the client bounds
            var clientBounds = _windowScroller.ScrollBarWindow.GetInfo().ClientBounds;

            // Use a region for steps 2 and 3
            using (var region = new Region(clientBounds))
            {
                // 2. exclude the children, if any
                foreach (var interopWindow in _windowScroller.ScrollBarWindow.GetChildren())
                {
                    region.Exclude(interopWindow.GetInfo().Bounds);
                }
                // 3. exclude the scrollbar, if it can be found
                if (_windowScroller.ScrollBar.HasValue)
                {
                    region.Exclude(_windowScroller.ScrollBar.Value.Bounds);
                }
                // Get the bounds of the region
                using (var screenGraphics = Graphics.FromHwnd(User32Api.GetDesktopWindow()))
                {
                    var rectangleF = region.GetBounds(screenGraphics);
                    clientBounds = new NativeRect((int)rectangleF.X, (int)rectangleF.Y, (int)rectangleF.Width, (int)rectangleF.Height);
                }
            }

            if (clientBounds.Width * clientBounds.Height <= 0)
            {
                return(null);
            }
            // Move the window to the start
            _windowScroller.Start();

            // Register a keyboard hook to make it possible to ESC the capturing
            var breakScroll  = false;
            var keyboardHook = KeyboardHook.KeyboardEvents
                               .Where(args => args.Key == VirtualKeyCodes.ESCAPE)
                               .Subscribe(args =>
            {
                args.Handled = true;
                breakScroll  = true;
            });
            Bitmap resultImage = null;

            try
            {
                // A delay to make the window move
                Application.DoEvents();
                Thread.Sleep(Delay);
                Application.DoEvents();

                if (_windowScroller.IsAtStart)
                {
                    using (var bitmapStitcher = new BitmapStitcher())
                    {
                        bitmapStitcher.AddBitmap(WindowCapture.CaptureRectangle(clientBounds));

                        // Loop as long as we are not at the end yet
                        while (!_windowScroller.IsAtEnd && !breakScroll)
                        {
                            // Next "page"
                            _windowScroller.Next();
                            // Wait a bit, so the window can update
                            Application.DoEvents();
                            Thread.Sleep(Delay);
                            Application.DoEvents();
                            // Capture inside loop
                            bitmapStitcher.AddBitmap(WindowCapture.CaptureRectangle(clientBounds));
                        }
                        resultImage = bitmapStitcher.Result();
                    }
                }
                else
                {
                    resultImage = WindowCapture.CaptureRectangle(clientBounds);
                }
            }
            catch (Exception ex)
            {
                Log.Error().WriteLine(ex);
            }
            finally
            {
                // Remove hook for escape
                keyboardHook.Dispose();
                // Try to reset location
                _windowScroller.Reset();
            }

            return(resultImage);
        }
예제 #4
0
 /// <summary>
 ///     Gets the Desktop window
 /// </summary>
 /// <returns>IInteropWindow for the desktop window</returns>
 public static IInteropWindow GetDesktopWindow()
 {
     return(InteropWindowFactory.CreateFor(User32Api.GetDesktopWindow()));
 }
        /// <summary>
        /// Creates a SafeWindowDcHandle for the Desktop
        /// </summary>
        /// <returns>SafeWindowDcHandle</returns>
        public static SafeWindowDcHandle FromDesktop()
        {
            var hWndDesktop = User32Api.GetDesktopWindow();

            return(FromWindow(hWndDesktop));
        }