You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 17:42:09