我正在使用以下代码捕获屏幕并将其复制到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();
}
3 回答
您的代码泄漏了CreateDC()返回的句柄 . 它必须通过调用DeleteDC()来释放 . 程序泄漏10,000个句柄后,Windows将不再提供它 . 您可以使用TaskMgr.exe,Processes选项卡诊断这些泄漏 . 查看选择列以添加句柄,用户对象和GDI对象的列 . GDI Objects正在稳步增长 .
使用Graphics.CopyFromScreen()绝对是遇到这种麻烦的较小方法 . 但是,它有一个错误 . 它和您当前的代码都不会捕获任何分层窗口 . 这需要CAPTUREBLT选项与托管代码中的BitBlt(),CopyPixelOperation.CaptureBlt选项 . CopyFromScreen()摸索了此选项,不允许您传递它 .
回到BitBlt()来解决这个问题 . 你会在this web page的答案中找到已知的工作代码 .
可能有一段时间GDI需要更多时间 . 要测试它,可以增加Timer.Interval .
但是为什么Petzold如此复杂?
然后在你的方法中:
这是重写的代码,将Petzold的代码与Hans Passant的例子混合在一起(仍然缺少CloseHandle(hBitmap)调用):