/// <summary>
        /// Place the surface as Format17 bitmap on the clipboard
        /// </summary>
        /// <param name="clipboardAccessToken">IClipboardAccessToken</param>
        /// <param name="surface">ISurface</param>
        public static void SetAsFormat17(this IClipboardAccessToken clipboardAccessToken, ISurface surface)
        {
            // Create the stream for the clipboard
            using (var dibV5Stream = new MemoryStream())
            {
                var  outputSettings = new SurfaceOutputSettings(OutputFormats.bmp, 100, false);
                bool dispose        = ImageOutput.CreateBitmapFromSurface(surface, outputSettings, out var bitmapToSave);
                // Create the BITMAPINFOHEADER
                var header = BitmapInfoHeader.Create(bitmapToSave.Width, bitmapToSave.Height, 32);
                // Make sure we have BI_BITFIELDS, this seems to be normal for Format17?
                header.Compression = BitmapCompressionMethods.BI_BITFIELDS;

                var headerBytes = BinaryStructHelper.ToByteArray(header);
                // Write the BITMAPINFOHEADER to the stream
                dibV5Stream.Write(headerBytes, 0, headerBytes.Length);

                // As we have specified BI_COMPRESSION.BI_BITFIELDS, the BitfieldColorMask needs to be added
                var colorMask = BitfieldColorMask.Create();
                // Create the byte[] from the struct
                var colorMaskBytes = BinaryStructHelper.ToByteArray(colorMask);
                Array.Reverse(colorMaskBytes);
                // Write to the stream
                dibV5Stream.Write(colorMaskBytes, 0, colorMaskBytes.Length);

                // Create the raw bytes for the pixels only
                var bitmapBytes = BitmapToByteArray(bitmapToSave);
                // Write to the stream
                dibV5Stream.Write(bitmapBytes, 0, bitmapBytes.Length);
                // Reset the stream to the beginning so it can be written
                dibV5Stream.Seek(0, SeekOrigin.Begin);
                // Set the DIBv5 to the clipboard DataObject
                clipboardAccessToken.SetAsStream("Format17", dibV5Stream);
                if (dispose)
                {
                    bitmapToSave.Dispose();
                }
            }
        }
Exemplo n.º 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);
        }
Exemplo n.º 3
0
        /// <summary>
        ///     Set an Image to the clipboard
        ///     This method will place images to the clipboard depending on the ClipboardFormats setting.
        ///     e.g. Bitmap which works with pretty much everything and type Dib for e.g. OpenOffice
        ///     because OpenOffice has a bug http://qa.openoffice.org/issues/show_bug.cgi?id=85661
        ///     The Dib (Device Indenpendend Bitmap) in 32bpp actually won't work with Powerpoint 2003!
        ///     When pasting a Dib in PP 2003 the Bitmap is somehow shifted left!
        ///     For this problem the user should not use the direct paste (=Dib), but select Bitmap
        /// </summary>
        public static void SetClipboardData(ISurface surface)
        {
            var dataObject = new DataObject();

            // This will work for Office and most other applications
            //ido.SetData(DataFormats.Bitmap, true, image);

            MemoryStream dibStream    = null;
            MemoryStream dibV5Stream  = null;
            MemoryStream pngStream    = null;
            Bitmap       bitmapToSave = null;
            var          disposeImage = false;

            try
            {
                var outputSettings = new SurfaceOutputSettings(OutputFormats.png, 100, false);
                // Create the image which is going to be saved so we don't create it multiple times
                disposeImage = ImageOutput.CreateBitmapFromSurface(surface, outputSettings, out bitmapToSave);
                try
                {
                    // Create PNG stream
                    if (CoreConfig.ClipboardFormats.Contains(ClipboardFormats.PNG))
                    {
                        pngStream = new MemoryStream();
                        // PNG works for e.g. Powerpoint
                        var pngOutputSettings = new SurfaceOutputSettings(OutputFormats.png, 100, false);
                        ImageOutput.SaveToStream(bitmapToSave, null, pngStream, pngOutputSettings);
                        pngStream.Seek(0, SeekOrigin.Begin);
                        // Set the PNG stream
                        dataObject.SetData(FORMAT_PNG, false, pngStream);
                    }
                }
                catch (Exception pngEx)
                {
                    Log.Error().WriteLine(pngEx, "Error creating PNG for the Clipboard.");
                }

                try
                {
                    if (CoreConfig.ClipboardFormats.Contains(ClipboardFormats.DIB))
                    {
                        using (var tmpBmpStream = new MemoryStream())
                        {
                            // Save image as BMP
                            var bmpOutputSettings = new SurfaceOutputSettings(OutputFormats.bmp, 100, false);
                            ImageOutput.SaveToStream(bitmapToSave, null, tmpBmpStream, bmpOutputSettings);

                            dibStream = new MemoryStream();
                            // Copy the source, but skip the "BITMAPFILEHEADER" which has a size of 14
                            dibStream.Write(tmpBmpStream.GetBuffer(), BITMAPFILEHEADER_LENGTH, (int)tmpBmpStream.Length - BITMAPFILEHEADER_LENGTH);
                        }

                        // Set the DIB to the clipboard DataObject
                        dataObject.SetData(DataFormats.Dib, true, dibStream);
                    }
                }
                catch (Exception dibEx)
                {
                    Log.Error().WriteLine(dibEx, "Error creating DIB for the Clipboard.");
                }

                // CF_DibV5
                try
                {
                    if (CoreConfig.ClipboardFormats.Contains(ClipboardFormats.DIBV5))
                    {
                        // Create the stream for the clipboard
                        dibV5Stream = new MemoryStream();

                        // Create the BITMAPINFOHEADER
                        var header = BitmapInfoHeader.Create(bitmapToSave.Width, bitmapToSave.Height, 32);
                        // Make sure we have BI_BITFIELDS, this seems to be normal for Format17?
                        header.Compression = BitmapCompressionMethods.BI_BITFIELDS;

                        var headerBytes = BinaryStructHelper.ToByteArray(header);
                        // Write the BITMAPINFOHEADER to the stream
                        dibV5Stream.Write(headerBytes, 0, headerBytes.Length);

                        // As we have specified BI_COMPRESSION.BI_BITFIELDS, the BitfieldColorMask needs to be added
                        var colorMask = BitfieldColorMask.Create();
                        // Create the byte[] from the struct
                        var colorMaskBytes = BinaryStructHelper.ToByteArray(colorMask);
                        Array.Reverse(colorMaskBytes);
                        // Write to the stream
                        dibV5Stream.Write(colorMaskBytes, 0, colorMaskBytes.Length);

                        // Create the raw bytes for the pixels only
                        var bitmapBytes = BitmapToByteArray(bitmapToSave);
                        // Write to the stream
                        dibV5Stream.Write(bitmapBytes, 0, bitmapBytes.Length);

                        // Set the DIBv5 to the clipboard DataObject
                        dataObject.SetData(FORMAT_17, true, dibV5Stream);
                    }
                }
                catch (Exception dibEx)
                {
                    Log.Error().WriteLine(dibEx, "Error creating DIB for the Clipboard.");
                }

                // Set the HTML
                if (CoreConfig.ClipboardFormats.Contains(ClipboardFormats.HTML))
                {
                    var tmpFile = ImageOutput.SaveToTmpFile(surface, new SurfaceOutputSettings(OutputFormats.png, 100, false), null);
                    var html    = GetHtmlString(surface, tmpFile);
                    dataObject.SetText(html, TextDataFormat.Html);
                }
                else if (CoreConfig.ClipboardFormats.Contains(ClipboardFormats.HTMLDATAURL))
                {
                    string html;
                    using (var tmpPngStream = new MemoryStream())
                    {
                        var pngOutputSettings = new SurfaceOutputSettings(OutputFormats.png, 100, false)
                        {
                            // Do not allow to reduce the colors, some applications dislike 256 color images
                            // reported with bug #3594681
                            DisableReduceColors = true
                        };
                        // Check if we can use the previously used image
                        if (bitmapToSave.PixelFormat != PixelFormat.Format8bppIndexed)
                        {
                            ImageOutput.SaveToStream(bitmapToSave, surface, tmpPngStream, pngOutputSettings);
                        }
                        else
                        {
                            ImageOutput.SaveToStream(surface, tmpPngStream, pngOutputSettings);
                        }
                        html = GetHtmlDataUrlString(surface, tmpPngStream);
                    }
                    dataObject.SetText(html, TextDataFormat.Html);
                }
            }
            finally
            {
                // we need to use the SetDataOject before the streams are closed otherwise the buffer will be gone!
                // Check if Bitmap is wanted
                if (CoreConfig.ClipboardFormats.Contains(ClipboardFormats.BITMAP))
                {
                    dataObject.SetImage(bitmapToSave);
                    // Place the DataObject to the clipboard
                    SetDataObject(dataObject, true);
                }
                else
                {
                    // Place the DataObject to the clipboard
                    SetDataObject(dataObject, true);
                }

                pngStream?.Dispose();
                dibStream?.Dispose();
                dibV5Stream?.Dispose();
                // cleanup if needed
                if (disposeImage)
                {
                    bitmapToSave?.Dispose();
                }
            }
        }