WPF中实现类VNC屏幕捕获出现异常,求原因及解决方案
WPF屏幕捕获异常的原因及解决方法
异常原因分析
结合你的代码,常见触发异常的原因有以下几点:
- 屏幕单位不匹配:
SystemParameters.PrimaryScreenWidth/Height是WPF的设备无关单位(基于96DPI),直接转成int作为屏幕像素尺寸会和实际屏幕分辨率不符,导致CopyFromScreen操作超出屏幕范围抛出异常。 - GDI+资源泄漏:
CaptureScreen方法中创建的Graphics对象未释放,加上频繁创建Bitmap且未及时销毁,会耗尽GDI+资源,引发内存或资源异常。 - 定时器线程的潜在问题:
System.Timers.Timer的Elapsed事件在后台线程执行,虽用Dispatcher.Invoke更新UI,但频繁跨线程传递Bitmap对象可能引发隐性线程安全问题。
解决方法
- 使用真实屏幕像素尺寸:改用
Screen.PrimaryScreen.Bounds获取屏幕真实像素宽高,避免单位转换错误。 - 释放GDI+资源:对
Graphics、Bitmap等实现IDisposable的对象使用using语句,确保资源及时释放。 - 改用DispatcherTimer:替换
System.Timers.Timer为DispatcherTimer,其Tick事件直接在UI线程执行,无需额外Dispatcher.Invoke,简化代码并避免跨线程问题。 - 优化Bitmap转BitmapImage逻辑:确保Bitmap在转换后被正确释放,避免内存泄漏。
修正后的完整代码
private DispatcherTimer timer; private void MainWindow_Loaded(object sender, RoutedEventArgs e) { timer = new DispatcherTimer(); timer.Interval = TimeSpan.FromMilliseconds(100); timer.Tick += Timer_Tick; timer.Start(); } private void Timer_Tick(object sender, EventArgs e) { var screenBounds = Screen.PrimaryScreen.Bounds; using (var bitmap = CaptureScreen(screenBounds.X, screenBounds.Y, screenBounds.Width, screenBounds.Height)) { img.Source = BitmapToImageSource(bitmap); } } public Bitmap CaptureScreen(int x, int y, int width, int height) { var image = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb); using (var graphics = Graphics.FromImage(image)) { graphics.CopyFromScreen(x, y, 0, 0, new System.Drawing.Size(width, height), CopyPixelOperation.SourceCopy); } return image; } private BitmapImage BitmapToImageSource(Bitmap bitmap) { using (var memory = new MemoryStream()) { bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Bmp); memory.Position = 0; var bitmapImage = new BitmapImage(); bitmapImage.BeginInit(); bitmapImage.StreamSource = memory; bitmapImage.CacheOption = BitmapCacheOption.OnLoad; bitmapImage.EndInit(); bitmapImage.Freeze(); // 冻结BitmapImage,允许跨线程访问并提升性能 return bitmapImage; } }
额外说明
- 调用
bitmapImage.Freeze()可将BitmapImage标记为不可修改,既避免跨线程异常,也能提升性能。 - 若需捕获多屏幕,可遍历
Screen.AllScreens获取每个屏幕的Bounds进行捕获。
内容的提问来源于stack exchange,提问作者CDrnly
相关产品推荐
相关产品推荐

