使用GetHBitmap的GDI +中的一般错误(WPF4/C#)

HeB*_*eBu 4 .net c# gdi+ wpf-4.0 c#-4.0

我正在使用以下代码捕获屏幕并将其复制到BitmapSource中.每400ms通过DispatcherTimer连续调用该方法.首先,我在.NET Framework 3.5中使用了此代码,然后切换到Framework 4.0.当程序运行一段时间(假设15分钟)时,它会在GetHBitmap调用期间突然崩溃,出现"GDI +中的通用错误".

当我切换到.NET 4.0时,我不得不注释掉CloseHandle()调用,它引发了一个SEHException.也许这会导致问题,也许不会.

所以,这是我的代码.我希望有人能帮帮忙...

// The code is based on an example by Charles Petzold
// http://www.charlespetzold.com/pwcs/ReadingPixelsFromTheScreen.html

// Import external Win32 functions

// BitBlt is used for the bit by bit block copy of the screen content
[DllImport("gdi32.dll")]
private static extern bool BitBlt(IntPtr hdcDst, int xDst, int yDst, int cx, int cy,
                                    IntPtr hdcSrc, int xSrc, int ySrc, uint ulRop);

// DeleteObject is used to delete the bitmap handle
[DllImport("gdi32.dll")]
private static extern bool DeleteObject(IntPtr hObject);

// CreateDC is used to create a graphics handle to the screen
[DllImport("gdi32.dll")]
private static extern IntPtr CreateDC(string lpszDriver, string lpszDevice, string lpszOutput, IntPtr lpInitData);

// CloseHandle is used to close the bitmap handle, which does not work with Framework 4 :(
// [DllImport("Kernel32")]
// private static extern bool CloseHandle(IntPtr handle);

public static void getBitmap(ref BitmapSource bms)
{
    // define the raster-operation code for the BitBlt method
    // SRCOPY copies the source directly to the destination
    const int SRCCOPY = 0x00CC0020;

    // The screenshot will be stored here
    Bitmap bm;

    // Get a Graphics object associated with the screen
    Screen s = UIHelper.getScreenHandle();
    Graphics grfxScreen = Graphics.FromHdc(CreateDC(null, s.DeviceName, null,
        IntPtr.Zero));

    // Create a bitmap the size of the screen.
    bm = new Bitmap((int)grfxScreen.VisibleClipBounds.Width,
                    (int)grfxScreen.VisibleClipBounds.Height, grfxScreen);

    // Create a Graphics object associated with the bitmap
    Graphics grfxBitmap = Graphics.FromImage(bm);

    // Get handles associated with the Graphics objects
    IntPtr hdcScreen = grfxScreen.GetHdc();
    IntPtr hdcBitmap = grfxBitmap.GetHdc();

    // Do the bitblt from the screen to the bitmap
    BitBlt(hdcBitmap, 0, 0, bm.Width, bm.Height,
            hdcScreen, 0, 0, SRCCOPY);

    // Release the device contexts.
    grfxBitmap.ReleaseHdc(hdcBitmap);
    grfxScreen.ReleaseHdc(hdcScreen);

    // convert the Bitmap to BitmapSource
    IntPtr hBitmap = bm.GetHbitmap();         // Application crashes here after a while...

    //System.Runtime.InteropServices.ExternalException was unhandled
    //  Message=Generic Error in GDI+.
    //  Source=System.Drawing
    //  ErrorCode=-2147467259
    //  StackTrace:
    //       at System.Drawing.Bitmap.GetHbitmap(Color background)
    //       at System.Drawing.Bitmap.GetHbitmap()

    if (bms != null) bms = null; // Dispose bms if it holds content
    bms = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
        hBitmap,
        IntPtr.Zero,
        Int32Rect.Empty,
        System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());

    // tidy up

    // CloseHandle throws SEHException using Framework 4
    // CloseHandle(hBitmap);

    DeleteObject(hBitmap);
    hBitmap = IntPtr.Zero;
    bm.Dispose();
    hdcBitmap = IntPtr.Zero;
    hdcScreen = IntPtr.Zero;
    grfxBitmap.Dispose();
    grfxScreen.Dispose();
    GC.Collect();

}
Run Code Online (Sandbox Code Playgroud)

Han*_*ant 10

您的代码泄漏了CreateDC()返回的句柄.它必须通过调用DeleteDC()来释放.程序泄漏10,000个句柄后,Windows将不再提供它.您可以使用TaskMgr.exe,Processes选项卡诊断这些泄漏.查看+选择列以添加句柄,用户对象和GDI对象的列.GDI Objects正在稳步增长.

使用Graphics.CopyFromScreen()绝对是遇到这种麻烦的较小方法.但是,它有一个错误.它和您当前的代码都不会捕获任何分层窗口.这需要CAPTUREBLT选项与托管代码中的BitBlt(),CopyPixelOperation.CaptureBlt选项.CopyFromScreen()摸索了此选项,不允许您传递它.

回到BitBlt()来解决这个问题.您将在此网页的答案中找到已知工作代码.