public void Start(int framesPerSecond)
        {
            string filename;
            if (recordingWindow != null) {
                string windowTitle = Regex.Replace(recordingWindow.Text, @"[^\x20\d\w]", "");
                if (string.IsNullOrEmpty(windowTitle)) {
                    windowTitle = "greenshot-recording";
                }
                filename = Path.Combine(conf.OutputFilePath, windowTitle + ".avi");

            } else {
                filename = Path.Combine(conf.OutputFilePath, "greenshot-recording.avi");
            }
            if (File.Exists(filename)) {
                try {
                    File.Delete(filename);
                } catch {}
            }
            LOG.InfoFormat("Capturing to {0}", filename);

            if (recordingWindow != null) {
                LOG.InfoFormat("Starting recording Window '{0}', {1}", recordingWindow.Text, recordingWindow.ClientRectangle);
                recordingSize = recordingWindow.ClientRectangle.Size;
            } else {
                LOG.InfoFormat("Starting recording rectangle {0}", recordingRectangle);
                recordingSize = recordingRectangle.Size;
            }
            if (recordingSize.Width % 8 > 0) {
                LOG.InfoFormat("Correcting width to be factor 8, {0} => {1}", recordingSize.Width, recordingSize.Width + (8-(recordingSize.Width % 8)));
                recordingSize = new Size(recordingSize.Width + (8-(recordingSize.Width % 8)), recordingSize.Height);
            }
            if (recordingSize.Height % 8 > 0) {
                LOG.InfoFormat("Correcting Height to be factor 8, {0} => {1}", recordingSize.Height, recordingSize.Height + (8-(recordingSize.Height % 8)));
                recordingSize = new Size(recordingSize.Width, recordingSize.Height + (8-(recordingSize.Height % 8)));
            }
            this.framesPerSecond = framesPerSecond;
            // "P/Invoke" Solution for capturing the screen
            hWndDesktop = User32.GetDesktopWindow();
            // get te hDC of the target window
            hDCDesktop = User32.GetWindowDC(hWndDesktop);
            // Make sure the last error is set to 0
            Win32.SetLastError(0);

            // create a device context we can copy to
            hDCDest = GDI32.CreateCompatibleDC(hDCDesktop);
            // Check if the device context is there, if not throw an error with as much info as possible!
            if (hDCDest == IntPtr.Zero) {
                // Get Exception before the error is lost
                Exception exceptionToThrow = CreateCaptureException("CreateCompatibleDC", recordingSize);
                // Cleanup
                User32.ReleaseDC(hWndDesktop, hDCDesktop);
                // throw exception
                throw exceptionToThrow;
            }

            // Create BitmapInfoHeader for CreateDIBSection
            BitmapInfoHeader bitmapInfoHeader = new BitmapInfoHeader(recordingSize.Width, recordingSize.Height, 32);

            // 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
            hDIBSection = GDI32.CreateDIBSection(hDCDesktop, ref bitmapInfoHeader, BitmapInfoHeader.DIB_RGB_COLORS, out bits0, IntPtr.Zero, 0);

            if (hDIBSection == IntPtr.Zero) {
                // Get Exception before the error is lost
                Exception exceptionToThrow = CreateCaptureException("CreateDIBSection", recordingSize);
                exceptionToThrow.Data.Add("hdcDest", hDCDest.ToInt32());
                exceptionToThrow.Data.Add("hdcSrc", hDCDesktop.ToInt32());

                // clean up
                GDI32.DeleteDC(hDCDest);
                User32.ReleaseDC(hWndDesktop, hDCDesktop);

                // Throw so people can report the problem
                throw exceptionToThrow;
            }
            // Create a GDI Bitmap so we can use GDI and GDI+ operations on the same memory
            GDIBitmap = new Bitmap(recordingSize.Width, recordingSize.Height, 32, PixelFormat.Format32bppArgb, bits0);
            // select the bitmap object and store the old handle
            hOldObject = GDI32.SelectObject(hDCDest, hDIBSection);
            stop = false;

            aviWriter = new AVIWriter();
            // Comment the following 2 lines to make the user select it's own codec
            //aviWriter.Codec = "msvc";
            //aviWriter.Quality = 99;

            aviWriter.FrameRate = framesPerSecond;
            aviWriter.Open(filename, recordingSize.Width, recordingSize.Height);

            // Start update check in the background
            backgroundTask = new Thread (new ThreadStart(CaptureFrame));
            backgroundTask.IsBackground = true;
            backgroundTask.Start();
        }
Exemple #2
0
        /// <summary>
        /// This method will use User32 code to capture the specified captureBounds from the screen
        /// </summary>
        /// <param name="captureBounds">Rectangle 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(Rectangle captureBounds)
        {
            Bitmap returnBitmap = null;
            if (captureBounds.Height <= 0 || captureBounds.Width <= 0)
            {
                LOG.Warn("Nothing to capture, ignoring!");
                return null;
            }
            else
            {
                LOG.Debug("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, Point.Empty, captureBounds.Size, CopyPixelOperation.CaptureBlt);
            // }
            // capture.Image = capturedBitmap;
            // capture.Location = captureBounds.Location;

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

                // create a device context we can copy to
                using (SafeCompatibleDCHandle safeCompatibleDCHandle = GDI32.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
                        Exception exceptionToThrow = CreateCaptureException("CreateCompatibleDC", captureBounds);
                        // throw exception
                        throw exceptionToThrow;
                    }
                    // Create BitmapInfoHeader for CreateDIBSection
                    BitmapInfoHeader bmi = new BitmapInfoHeader(captureBounds.Width, captureBounds.Height, 24);

                    // 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.
                    using (SafeDibSectionHandle safeDibSectionHandle = GDI32.CreateDIBSection(desktopDCHandle, ref bmi, BitmapInfoHeader.DIB_RGB_COLORS, out bits0, IntPtr.Zero, 0))
                    {
                        if (safeDibSectionHandle.IsInvalid)
                        {
                            // Get Exception before the error is lost
                            Exception 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;
                        }
                        else
                        {
                            // select the bitmap object and store the old handle
                            using (SafeSelectObjectHandle selectObject = safeCompatibleDCHandle.SelectObject(safeDibSectionHandle))
                            {
                                // bitblt over (make copy)
                                GDI32.BitBlt(safeCompatibleDCHandle, 0, 0, captureBounds.Width, captureBounds.Height, desktopDCHandle, captureBounds.X, captureBounds.Y, CopyPixelOperation.SourceCopy | CopyPixelOperation.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...
                            bool success = false;
                            ExternalException exception = null;
                            for (int i = 0; i < 3; i++)
                            {
                                try
                                {
                                    // Collect all screens inside this capture
                                    List<Screen> screensInsideCapture = new List<Screen>();
                                    foreach (Screen screen in Screen.AllScreens)
                                    {
                                        if (screen.Bounds.IntersectsWith(captureBounds))
                                        {
                                            screensInsideCapture.Add(screen);
                                        }
                                    }
                                    // Check all all screens are of an equal size
                                    bool offscreenContent = false;
                                    using (Region captureRegion = new Region(captureBounds))
                                    {
                                        // Exclude every visible part
                                        foreach (Screen screen in screensInsideCapture)
                                        {
                                            captureRegion.Exclude(screen.Bounds);
                                        }
                                        // If the region is not empty, we have "offscreenContent"
                                        using (Graphics screenGraphics = Graphics.FromHwnd(User32.GetDesktopWindow()))
                                        {
                                            offscreenContent = !captureRegion.IsEmpty(screenGraphics);
                                        }
                                    }
                                    // Check if we need to have a transparent background, needed for offscreen content
                                    if (offscreenContent)
                                    {
                                        using (Bitmap tmpBitmap = Bitmap.FromHbitmap(safeDibSectionHandle.DangerousGetHandle()))
                                        {
                                            // Create a new bitmap which has a transparent background
                                            returnBitmap = ImageHelper.CreateEmpty(tmpBitmap.Width, tmpBitmap.Height, PixelFormat.Format32bppArgb, Color.Transparent, tmpBitmap.HorizontalResolution, tmpBitmap.VerticalResolution);
                                            // Content will be copied here
                                            using (Graphics graphics = Graphics.FromImage(returnBitmap))
                                            {
                                                // For all screens copy the content to the new bitmap
                                                foreach (Screen screen in Screen.AllScreens)
                                                {
                                                    Rectangle screenBounds = screen.Bounds;
                                                    // Make sure the bounds are offsetted to the capture 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 = Bitmap.FromHbitmap(safeDibSectionHandle.DangerousGetHandle());
                                    }
                                    // We got through the capture without exception
                                    success = true;
                                    break;
                                }
                                catch (ExternalException ee)
                                {
                                    LOG.Warn("Problem getting bitmap at try " + i + " : ", ee);
                                    exception = ee;
                                }
                            }
                            if (!success)
                            {
                                LOG.Error("Still couldn't create Bitmap!");
                                throw exception;
                            }
                        }
                    }
                }
            }
            return returnBitmap;
        }
        /// <summary>
        /// This method will use User32 code to capture the specified captureBounds from the screen
        /// </summary>
        /// <param name="captureBounds">Rectangle 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(Rectangle captureBounds)
        {
            Bitmap returnBitmap = null;
            if (captureBounds.Height <= 0 || captureBounds.Width <= 0) {
                LOG.Warn("Nothing to capture, ignoring!");
                return null;
            } else {
                LOG.Debug("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, Point.Empty, captureBounds.Size, CopyPixelOperation.CaptureBlt);
            // }
            // capture.Image = capturedBitmap;
            // capture.Location = captureBounds.Location;

            // "P/Invoke" Solution for capturing the screen
            IntPtr hWndDesktop = User32.GetDesktopWindow();
            // get te hDC of the target window
            IntPtr hDCDesktop = User32.GetWindowDC(hWndDesktop);

            // Make sure the last error is set to 0
            Win32.SetLastError(0);

            // create a device context we can copy to
            IntPtr hDCDest = GDI32.CreateCompatibleDC(hDCDesktop);
            // Check if the device context is there, if not throw an error with as much info as possible!
            if (hDCDest == IntPtr.Zero) {
                // Get Exception before the error is lost
                Exception exceptionToThrow = CreateCaptureException("CreateCompatibleDC", captureBounds);
                // Cleanup
                User32.ReleaseDC(hWndDesktop, hDCDesktop);
                // throw exception
                throw exceptionToThrow;
            }

            // Create BitmapInfoHeader for CreateDIBSection
            BitmapInfoHeader bmi = new BitmapInfoHeader(captureBounds.Width, captureBounds.Height, 24);

            // 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.
            IntPtr hDIBSection = GDI32.CreateDIBSection(hDCDesktop, ref bmi, BitmapInfoHeader.DIB_RGB_COLORS, out bits0, IntPtr.Zero, 0);

            if (hDIBSection == IntPtr.Zero) {
                // Get Exception before the error is lost
                Exception exceptionToThrow = CreateCaptureException("CreateDIBSection", captureBounds);
                exceptionToThrow.Data.Add("hdcDest", hDCDest.ToInt32());
                exceptionToThrow.Data.Add("hdcSrc", hDCDesktop.ToInt32());

                // clean up
                GDI32.DeleteDC(hDCDest);
                User32.ReleaseDC(hWndDesktop, hDCDesktop);

                // Throw so people can report the problem
                throw exceptionToThrow;
            } else {
                // select the bitmap object and store the old handle
                IntPtr hOldObject = GDI32.SelectObject(hDCDest, hDIBSection);

                // bitblt over (make copy)
                GDI32.BitBlt(hDCDest, 0, 0, captureBounds.Width, captureBounds.Height, hDCDesktop, captureBounds.X,  captureBounds.Y, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt);

                // restore selection (old handle)
                GDI32.SelectObject(hDCDest, hOldObject);
                // clean up
                GDI32.DeleteDC(hDCDest);
                User32.ReleaseDC(hWndDesktop, hDCDesktop);

                // 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...
                bool success = false;
                ExternalException exception = null;
                for(int i = 0; i < 3; i++) {
                    try {
                        // assign image to Capture, the image will be disposed there..
                        returnBitmap = Bitmap.FromHbitmap(hDIBSection);
                        success = true;
                        break;
                    } catch (ExternalException ee) {
                        LOG.Warn("Problem getting bitmap at try " + i + " : ", ee);
                        exception = ee;
                    }
                }
                if (!success) {
                    LOG.Error("Still couldn't create Bitmap!");
                    throw exception;
                }
                // free up the Bitmap object
                GDI32.DeleteObject(hDIBSection);

            }
            return returnBitmap;
        }
 public static extern SafeDibSectionHandle CreateDIBSection(SafeHandle hdc, ref BitmapInfoHeader bmi, uint Usage, out IntPtr bits, IntPtr hSection, uint dwOffset);
 public static extern IntPtr CreateDIBSection(IntPtr hdc, ref BitmapInfoHeader bmi, uint Usage, out IntPtr bits, IntPtr hSection, uint dwOffset);