C# PrintWindow - GetHdc 在多次迭代后崩溃

C# PrintWindow - GetHdc crashes after many iterations

这里是第一个问题,所以如果我可以改进此帖子,请随时告诉我 :) 我目前正在用 C# 编写一个相当简单的 .Net 应用程序,它使用 "user32.dll" 中的 "PrintWindow" 来截取其他应用程序的屏幕截图,即使它 运行 落后于另一个 window.

我的目标是让我的程序运行无休止地/很长一段时间,但我遇到了我无法解决的问题。

在大约 10.000 个屏幕截图时,我的应用程序总是崩溃。这是我在控制台应用程序中用于重现该错误及其附带的错误的代码:

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

在我的实际应用程序中,显然不应该如此快速地捕获图像,但几个小时后就会出现同样的错误。

我从这里编写重要代码:https://codereview.stackexchange.com/questions/29364/capturing-and-taking-a-screenshot-of-a-window-in-a-loop

我是否必须为我的项目放弃 PrintWindow?我宁愿坚持使用它,因为这是迄今为止我发现的唯一捕获后台 window 的方法。

好的! 我发现了问题,希望这对将来的人有帮助。在 GDIView 的帮助下,我发现我的应用程序泄漏了 "DC" 个对象。如果创建了超过 10.000 个对象(我应该首先查看),GDI 将拒绝工作。 之后没有被删除的DC隐藏在下面一行:

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

如果添加以下引用:

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

并像这样修改代码:

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

然后DC对象被正确删除,程序不再超过10.000个DC对象,因此不会再崩溃。