Search code examples
c#bitmapscreenshotargumentexception

C# PrintWindow - GetHdc crashes after many iterations


First question on here, so if I can improve this posting feel free to tell me :) I'm currently programming a rather simple .Net application in C# that uses "PrintWindow" from "user32.dll" in order to take screenshots of an other application even if it runs behind another window.

I aim to let my program run endlessly / for a long period of time, but I encounter I problem I cannot solve.

At around 10.000 Screenshots my application always crashes. Here is the Code I used in a console application to reproduce the bug and the error that comes with it:

class Program
{
    /* Get Image even if Process is running behind another window ******************* */
    [DllImport("user32.dll")]
    public static extern bool PrintWindow(IntPtr hwnd, IntPtr hdcBlt, uint nFlags);
    [DllImport("user32.dll")]
    public static extern IntPtr GetWindowDC(IntPtr hWnd);
    /* ****************************************************************************** */

    static void Main(string[] args)
    {
        Process process = ReturnProcess();
        int counter = 0;

        Console.WriteLine(RotMG.ToString());

        while (true)
        {
            Bitmap bmpTest = CaptureWindow(RotMG.MainWindowHandle);
            bmpTest.Dispose();

            counter++;
            Console.WriteLine(counter.ToString());
        }
    }

    private static Process ReturnProcess()
    {
        Process[] processes = Process.GetProcessesByName("desiredProcess");
        return processes[0];
    }

    public static Bitmap CaptureWindow(IntPtr hWnd)
    {
        Rectangle rctForm = System.Drawing.Rectangle.Empty;
        using (Graphics grfx = Graphics.FromHdc(GetWindowDC(hWnd)))
        {
            rctForm = Rectangle.Round(grfx.VisibleClipBounds);
        }
        Bitmap pImage = new Bitmap(rctForm.Width, rctForm.Height);
        Graphics graphics = Graphics.FromImage(pImage);
        IntPtr hDC = graphics.GetHdc();   
        try
        {
            PrintWindow(hWnd, hDC, (uint)0);
        }
        finally
        {
            graphics.ReleaseHdc(hDC);
            graphics.Dispose();
        }
        return pImage;
    }
}

IntPtr hDC = graphics.GetHdc(); System.ArgumentException: Parameter not valid

In my real application it obviously is not supposed to capture images that fast, but the same error occurs after an a few hours.

I code the important code from here: https://codereview.stackexchange.com/questions/29364/capturing-and-taking-a-screenshot-of-a-window-in-a-loop

Do I have to ditch PrintWindow for my project? I would rather stick to it as it is the only way I found so far to capture a window which is in background.


Solution

  • All right! I found the problem, hopefully this helps someone in the future. With the help of GDIView I found that my application leaked "DC" objects. GDI refuses to work if more than 10.000 Objects are created (which I should have looked up in the first place). The DC that is not being deleted afterwards hides in the following line:

    using (Graphics grfx = Graphics.FromHdc(GetWindowDC(hWnd)))
    

    If you add the following reference:

    [DllImport("gdi32.dll")]
    static extern IntPtr DeleteDC(IntPtr hDc);
    

    and modify the code like this:

    IntPtr WindowDC = GetWindowDC(hWnd);
    using (Graphics grfx = Graphics.FromHdc(WindowDC))
    {
        rctForm = Rectangle.Round(grfx.VisibleClipBounds);
    }
    DeleteDC(WindowDC);
    

    Then the DC object is deleted correctly, the program no longer exceeds 10.000 DC objects and thus does not crash anymore.