views:

544

answers:

2

Hi there,

I need to take print-screens of a Windows application very very fast to make a video out of it... I have been using C# all the way, but I'm open to any language in which this process may be faster.

I have used many techniques:

  • .net functions: Bitmap.CopyFromScreen()
  • GDI
  • Direct3d/DirectX

The fastest I've got was using GDI and still I get less than 10 fotograms per second. I would need a bit more than that, at least 20 or 30...

It seems very strange to me that such a simple operation is so demanding. And it looks as if using a faster cpu doesn't change the situation.

What can I do? Is it possible to directly capture the drawing of an application using gdi or something? Or maybe even lower-level functions to catch the info being thrown to the graphics card?

Any light on this issue would be pretty much appreciated. Thanks a lot

+1  A: 

A lot of programs use a driver and allows your application to hook into the lower level display routines. I'm not exactly sure how this is done, but it is possible. Here is a starting point on writing Windows driver. http://msdn.microsoft.com/en-us/library/ms809956.aspx

Here is something I just found via Google: http://www.hmelyoff.com/index.php?section=17

Daniel A. White
Why did i get voted down?
Daniel A. White
Ah, the eternal question.
bzlm
+1  A: 

You probably want to use something like Camtasia. Depends on why you're making the video.

I use a rewritten version of Jeff's User-Friendly Exception Handling, and he uses BitBlt from GDI to capture screenshots. Seems fast enough to me, but I haven't benchmarked it, and we just use it for one-at-a-time shots when there's an unhandled exception thrown.

#region Win32 API screenshot calls

// Win32 API calls necessary to support screen capture
[DllImport("gdi32", EntryPoint = "BitBlt", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int BitBlt(int hDestDC, int x, int y, int nWidth, int nHeight, int hSrcDC, int xSrc,
                                 int ySrc, int dwRop);

[DllImport("user32", EntryPoint = "GetDC", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int GetDC(int hwnd);

[DllImport("user32", EntryPoint = "ReleaseDC", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int ReleaseDC(int hwnd, int hdc);

#endregion

private static ImageFormat screenshotImageFormat = ImageFormat.Png;

/// <summary>
/// Takes a screenshot of the desktop and saves to filename and format specified
/// </summary>
/// <param name="fileName"></param>
private static void TakeScreenshotPrivate(string fileName)
{
    Rectangle r = Screen.PrimaryScreen.Bounds;

    using (Bitmap bitmap = new Bitmap(r.Right, r.Bottom))
    {
        const int SRCCOPY = 13369376;

        using (Graphics g = Graphics.FromImage(bitmap))
        {
            // Get a device context to the windows desktop and our destination  bitmaps
            int hdcSrc = GetDC(0);
            IntPtr hdcDest = g.GetHdc();

            // Copy what is on the desktop to the bitmap
            BitBlt(hdcDest.ToInt32(), 0, 0, r.Right, r.Bottom, hdcSrc, 0, 0, SRCCOPY);

            // Release device contexts
            g.ReleaseHdc(hdcDest);
            ReleaseDC(0, hdcSrc);

            string formatExtension = screenshotImageFormat.ToString().ToLower();
            string expectedExtension = string.Format(".{0}", formatExtension);

            if (Path.GetExtension(fileName) != expectedExtension)
            {
                fileName += expectedExtension;
            }

            switch (formatExtension)
            {
                case "jpeg":
                    BitmapToJPEG(bitmap, fileName, 80);
                    break;
                default:
                    bitmap.Save(fileName, screenshotImageFormat);
                    break;
            }

            // Save the complete path/filename of the screenshot for possible later use
            ScreenshotFullPath = fileName;
        }
    }
}

/// <summary>
/// Save bitmap object to JPEG of specified quality level
/// </summary>
/// <param name="bitmap"></param>
/// <param name="fileName"></param>
/// <param name="compression"></param>
private static void BitmapToJPEG(Image bitmap, string fileName, long compression)
{
    EncoderParameters encoderParameters = new EncoderParameters(1);
    ImageCodecInfo codecInfo = GetEncoderInfo("image/jpeg");

    encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, compression);
    bitmap.Save(fileName, codecInfo, encoderParameters);
}
Chris Doggett
Chris, the gdi solution I did is very similar to the one you posted, but since it has some differences I decided to try it out. However, it does not recognize the functions "GetDC", "BiBlt" and "ReleaseDC". Are these functions written by you? Could you provide me with them?
Those functions are the ones referenced by the DllImport attributes. I believe those should reference gdi32.dll and user32.dll automatically.
Chris Doggett