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

1次阅读
没有评论

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

image.webp

背景痛点

在开发需要录屏功能的应用时,很多 C# 开发者首先想到的是第三方库,比如 OpenCV、FFmpeg 等。但这些方案往往存在几个明显的问题:

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

  • 依赖重:需要打包大量 DLL 文件,显著增加应用体积
  • 授权复杂:某些库存在 GPL 协议风险
  • 性能开销:跨语言调用导致帧率不稳定
  • 兼容性问题:在不同 Windows 版本表现不一致

技术方案对比

Windows 平台其实提供了多种原生录屏方案,这里简单对比三种主流方案:

  1. Windows.Graphics.Capture API(推荐)
  2. 优点:系统原生支持,内存占用低,支持 DX12 纹理
  3. 缺点:仅限 Win10 1809+ 版本

  4. DirectX 抓取

  5. 优点:高性能,可获取原始纹理
  6. 缺点:实现复杂,需要处理 DX 资源管理

  7. FFmpeg+gdigrab

  8. 优点:跨平台兼容
  9. 缺点:CPU 占用高,延迟明显

核心实现步骤

1. 准备工作

首先确保项目满足以下条件:

  • 目标平台设置为 x64
  • 启用不安全代码(项目属性 > 生成)
  • 添加 NuGet 包:Microsoft.Windows.SDK.Contracts

2. 声明 P /Invoke

[DllImport("windows.graphics.capture.dll")]
private static extern IntPtr CreateCaptureItemForWindow(
    IntPtr window, 
    ref Guid iid);

[DllImport("d3d11.dll")]
private static extern int D3D11CreateDevice(
    IntPtr adapter,
    int driverType,
    IntPtr software,
    uint flags,
    IntPtr featureLevels,
    uint featureLevelsCount,
    uint sdkVersion,
    out IntPtr device,
    out IntPtr featureLevel,
    out IntPtr immediateContext);

3. 权限检查

if (!GraphicsCaptureSession.IsSupported())
{throw new NotSupportedException("当前系统版本不支持 GraphicsCapture");
}

完整代码实现

下面是核心录制类的实现(带关键注释):

public class ScreenRecorder : IDisposable
{
    // 建议帧率范围:30-60
    private const int TargetFrameRate = 60; 
    private GraphicsCaptureItem _captureItem;
    private Direct3D11CaptureFramePool _framePool;
    private GraphicsCaptureSession _session;

    public async Task StartCaptureAsync(IntPtr hwnd)
    {
        // 创建 DX11 设备
        D3D11CreateDevice(...);

        // 初始化帧池
        _framePool = Direct3D11CaptureFramePool.Create(
            device,
            DirectXPixelFormat.B8G8R8A8UIntNormalized,
            2, // 缓冲帧数
            _captureItem.Size);

        // 帧到达事件处理
        _framePool.FrameArrived += (s, e) => 
        {using (var frame = _framePool.TryGetNextFrame())
            {// 处理帧数据...}
        };

        // 开始录制
        _session = _framePool.CreateCaptureSession(_captureItem);
        _session.StartCapture();}

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

性能优化技巧

  1. 内存复用策略
  2. 重用 DX11 纹理资源
  3. 实现 IDisposable 及时释放非托管资源

  4. 线程模型选择

  5. UI 操作必须在主线程
  6. 帧处理建议用 ThreadPool

  7. 帧率控制

  8. 根据内容类型动态调整:
    • 静态内容:30fps
    • 游戏 / 视频:60fps

生产环境避坑指南

  1. UWP 权限问题
  2. 需要在 Package.appxmanifest 声明 screenCapture 能力
  3. 对于桌面程序,需调用Windows.Graphics.Capture.GraphicsCaptureAccess.RequestAccessAsync()

  4. 多显示器适配

  5. 使用 DisplayMonitor 类获取所有显示器信息
  6. 捕获特定显示器时需指定 RECT 区域

  7. 内存泄漏排查

  8. 确保所有 IDisposable 对象都被释放
  9. 使用 DX 调试层检查资源泄漏

延伸思考

掌握了基础录屏功能后,可以尝试以下进阶方向:

  1. 添加音频采集(通过 WASAPI)
  2. 支持 HDR 内容捕获
  3. 实现鼠标高亮效果
  4. 添加实时标注功能

参考资料

希望这篇指南能帮助你避开我踩过的那些坑。如果遇到任何问题,欢迎在评论区交流讨论!

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