C#调用Windows截图工具实现高效录屏:原理与实战避坑指南

1次阅读
没有评论

共计 2699 个字符,预计需要花费 7 分钟才能阅读完成。

image.webp

背景痛点

在开发需要录屏功能的 C# 应用时,很多开发者首先想到的是第三方库,比如 OBS 的封装库或者 FFmpeg。这些方案虽然功能强大,但也存在一些明显的痛点:

C# 调用 Windows 截图工具实现高效录屏:原理与实战避坑指南

  • 依赖重:需要附带大量 DLL 文件或运行时环境,显著增加应用体积
  • 授权复杂:部分库存在 GPL 协议风险,商业应用需谨慎处理
  • 性能开销:多层抽象导致 CPU/ 内存占用较高
  • 兼容性问题:不同 Windows 版本可能表现不一致

Windows 10 1809+ 自带的 Windows.Graphics.Capture API 恰好能解决这些问题,它作为系统原生组件,具有无额外依赖、硬件加速等优势。

技术方案对比

先横向对比几种常见录屏方案的特点:

  1. Windows.Graphics.Capture
  2. 优势:系统原生支持、硬件加速、内存占用低
  3. 局限:仅限 Win10 1809+、需处理 UWP 权限

  4. DirectX 抓取

  5. 优势:极致性能、帧率可控性强
  6. 局限:开发复杂度高、需处理 DXGI 资源管理

  7. FFmpeg+GDI

  8. 优势:跨平台、功能全面
  9. 局限:CPU 占用高、延迟明显

对于大多数 C# 应用而言,Windows API 方案在维护成本和性能之间取得了最佳平衡。

核心实现步骤

1. 环境准备

首先确认项目配置:

  • 目标平台至少设置为 Windows 10 (10.0.17763.0)
  • 启用不安全代码(项目属性 > 生成)
  • 添加 NuGet 包 Microsoft.Windows.CsWin32

2. P/Invoke 声明

由于这是 UWP API,我们需要通过 COM 交互方式调用:

// 使用 CsWin32 自动生成 API 声明
using Windows.Win32;
using Windows.Win32.Graphics.Capture;
using Windows.Win32.Graphics.Direct3D11;

3. 权限检查

在开始捕获前必须验证系统支持性:

if (!GraphicsCaptureSession.IsSupported()) 
{throw new NotSupportedException("需要 Windows 10 1809 或更高版本");
}

完整代码实现

下面是一个带有完整异常处理的捕获类示例:

public class ScreenRecorder : IDisposable
{
    // 核心 COM 接口
    private GraphicsCaptureItem _captureItem;
    private Direct3D11CaptureFramePool _framePool;
    private GraphicsCaptureSession _session;

    // 帧处理回调
    public event Action<IDirect3DSurface> FrameArrived;

    public async Task StartCaptureAsync(IntPtr hwnd)
    {
        // 1. 创建捕获项
        var factory = new GraphicsCaptureItemInterop();
        _captureItem = factory.CreateForWindow(hwnd);

        // 2. 初始化 D3D 设备(省略 D3D 设备创建代码)var d3dDevice = ...;

        // 3. 创建帧池(推荐 60FPS)_framePool = Direct3D11CaptureFramePool.Create(
            d3dDevice,
            DirectXPixelFormat.B8G8R8A8UIntNormalized,
            2,
            _captureItem.Size);

        // 4. 注册帧事件
        _framePool.FrameArrived += OnFrameArrived;

        // 5. 启动会话
        _session = _framePool.CreateCaptureSession(_captureItem);
        _session.StartCapture();}

    private void OnFrameArrived(Direct3D11CaptureFramePool sender, object args)
    {using var frame = sender.TryGetNextFrame();
        FrameArrived?.Invoke(frame.Surface);
    }

    public void Dispose()
    {_session?.Dispose();
        _framePool?.Dispose();}
}

性能优化技巧

内存复用策略

由于每帧都会产生新的 IDirect3DSurface,建议实现对象池:

// 使用 ConcurrentBag 实现简单对象池
private ConcurrentBag<IDirect3DSurface> _surfacePool = new();

private IDirect3DSurface GetSurface()
{return _surfacePool.TryTake(out var surface) 
        ? surface 
        : CreateNewSurface();}

线程模型选择

根据场景选择合适线程模型:

  1. UI 密集型应用:使用 DispatcherQueueController 创建专用 UI 线程
  2. 后台服务:采用 ThreadPool+ManualResetEvent 组合

三大常见问题解决方案

1. UWP 权限问题

错误表现:”Access denied” 异常
解决方法:

  • 清单文件添加<rescap:Capability Name="graphicsCapture"/>
  • 确保调用进程有 GraphicsCaptureProgrammatic 权限

2. 多显示器适配

处理方案:

// 获取所有显示器
var monitors = MonitorEnumerationHelper.GetMonitors();

// 选择主显示器
var primary = monitors.First(m => m.IsPrimary);
_captureItem = factory.CreateForMonitor(primary.Hmon);

3. 光标闪烁问题

通过设置捕获属性解决:

_session.IsCursorCaptureEnabled = false; // 禁用光标捕获

延伸开发方向

  1. 音频采集:结合 Windows.Media.Audio 捕获系统声音
  2. HDR 支持:检查 DXGI_OUTDUPL_METADATA_TYPE_HDR_METADATA
  3. 鼠标高亮效果:通过 Direct2D 实时绘制光标特效

官方资源

通过上述方案,我们实现了仅依赖系统组件的轻量级录屏方案。在实际项目中,建议结合 MediaFoundation 进行视频编码,可以达到更好的性能表现。

正文完
 0
评论(没有评论)