Windows Graphics Capture (WGC) 屏幕捕获简介简介Windows.Graphics.Capture是微软在 Windows 10 1903 新增的一套官方屏幕捕获屏幕录制API。它是WinRT APIWindows Runtime可以在 C、C#、Rust 等语言中使用。相比传统的 GDI 截屏、DXGI Desktop DuplicationWGC 具有以下优势官方支持兼容性好性能优秀GPU 加速原生支持HDR支持窗口和显示器捕获WGC 完整使用流程┌─────────────────────────────────────────────────────────────┐ │ WGC 捕获流程 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 1. 创建 D3D11 Device │ │ D3D11CreateDevice() BGRA_SUPPORT │ │ ↓ │ │ 2. 获取捕获目标 │ │ GraphicsCaptureItem (窗口/显示器) │ │ ↓ │ │ 3. 创建 FramePool │ │ Direct3D11CaptureFramePool::Create() │ │ ↓ │ │ 4. 创建 CaptureSession │ │ framePool.CreateCaptureSession(item) │ │ ↓ │ │ 5. 注册帧回调 │ │ framePool.FrameArrived OnFrameArrived │ │ ↓ │ │ 6. 开始捕获 │ │ session.StartCapture() │ │ ↓ │ │ 7. 处理帧数据 │ │ frame framePool.TryGetNextFrame() │ │ ↓ │ │ 8. 停止捕获 │ │ session.Close() / framePool.Close() │ │ │ └─────────────────────────────────────────────────────────────┘基础概念 QAWin32 与 WinRT 的区别Win32WinRTWindows 最传统、最基础的系统 API现代 Windows Runtime API使用 C/C 接口C# 可通过 P/Invoke 调用可使用 C/WinRT、C# 调用控制台程序、DLL 项目默认是 Win32Win32 程序中可使用 C/WinRT 调用 WinRT具备系统级能力权限最大有沙箱机制不能做强力系统操作WPF 与 UWP 的区别WPFWindows Presentation Foundation基于 .NET、XAML用于构建桌面 UI 的 C# 框架UWPUniversal Windows Platform使用 WinRT 作为底层的一种 App 类型主要使用 C# 开发核心组件详解CaptureSession - 捕获生命周期控制器使用 WGC 的重要步骤之一就是创建CaptureSession其功能是控制捕获的生命周期是捕获的遥控器。主要功能开始捕获StartCapture()停止捕获Close()设置是否显示红框IsCursorCaptureEnabled指定捕获目标窗口/桌面Direct3D11CaptureFramePool - 帧数据入口Direct3D11CaptureFramePool是 WGC 捕获帧的唯一入口用户只能通过它来获取 WGC 捕获的帧。注意虽然名字中有 Direct3D11但它是 WGC 的组件不是 D3D11 的。之所以叫这个名字是因为输出帧是 D3D11 Texture。核心特性这是一个FIFO 队列队列满时新帧会被丢弃保证系统捕获线程永远不会被用户代码阻塞WGC 捕获数据流系统捕获到新帧 ↓ FramePool 内部存储该帧最多 N 帧 ↓ 触发 FrameArrived 回调 ↓ 用户调用 TryGetNextFrame() 获取帧FramePool 的两种创建方式方法特点适用场景Create()需要 DispatcherQueueFrameArrived 在当前线程触发有 UI 消息循环的应用CreateFreeThreaded()不需要 DispatcherQueue回调在 FramePool 内部线程触发后台服务、无 UI 应用CreateFreeThreaded 注意事项如果回调函数耗时不会影响系统底层 GPU 生成帧但会导致丢帧影响捕获结果GraphicsCaptureItem - 捕获目标句柄GraphicsCaptureItem表示你想让 Windows 进行屏幕捕获的目标对象可以是一个窗口HWND一个显示器Monitor一个虚拟桌面区域Windows 11一个 AppWindowWinUI它不是纹理不是帧不是画面本身。它是一个 WinRT 对象WinRT COM用于告诉 WGC 你想捕获什么。IDXGISwapChain3 - 交换链接口交换链SwapChain是 Direct3D 应用用来存放渲染的帧BackBuffer将 BackBuffer 显示到屏幕Present管理帧之间的切换swap / flip如果不需要将捕获的帧渲染出来就不需要 IDXGISwapChain3。窗口录制 vs 显示器录制特性窗口录制显示器录制捕获范围单个窗口客户区整个显示器性能开销较低较高DPI 感知自动处理需要处理隐私保护仅捕获窗口内容捕获所有可见内容适用场景单应用录制、远程桌面全屏录制、直播推流窗口大小变化处理当捕获的窗口大小改变时需要重建 FramePoolvoidOnItemClosed(GraphicsCaptureItemconst,IInspectableconst){// 窗口关闭时的处理}// 监听窗口大小变化item.Closed({this,OnItemClosed});// 窗口大小变化时需要// 1. 关闭旧的 FramePool// 2. 使用新的尺寸创建 FramePool// 3. 重新创建 CaptureSession帧率控制与丢帧处理FramePool 缓冲区大小// 创建 FramePool 时指定缓冲区大小autoframePoolDirect3D11CaptureFramePool::Create(device,DirectXPixelFormat::B8G8R8A8UIntNormalized,2,// 缓冲区大小建议 2-3 帧item.Size());缓冲区大小选择建议1 帧最低延迟但容易丢帧2-3 帧平衡延迟和稳定性推荐4 帧更稳定但延迟增加丢帧原因分析原因解决方案回调处理时间过长优化帧处理逻辑减少回调内工作量缓冲区过小适当增加 FramePool 缓冲区大小GPU 负载过高降低分辨率或帧率内存不足及时释放帧资源避免内存累积帧率限制方案如果需要限制捕获帧率如 30fps可以在回调中控制std::chrono::steady_clock::time_point lastFrameTime;constautoframeIntervalstd::chrono::milliseconds(33);// ~30fpsvoidOnFrameArrived(Direct3D11CaptureFramePoolconstsender,IInspectableconst){autonowstd::chrono::steady_clock::now();if(now-lastFrameTimeframeInterval){// 跳过此帧autoframesender.TryGetNextFrame();return;// 帧会自动释放}lastFrameTimenow;// 正常处理帧autoframesender.TryGetNextFrame();ProcessFrame(frame);}高级特性Dirty Region 机制Dirty Region脏矩形每一帧窗口可能只有一部分变化这些变化的区域就叫 Dirty Region。通过TryGetNextFrame()拿到的永远是一张完整的 2D 纹理。开启 Dirty Region 后系统会通过其他字段告诉你哪些地方发生了变化你可以选择是否使用这个信息进行优化。HDR 兼容性在创建 FramePool 时指定格式// SDR 格式推荐兼容性好DirectXPixelFormat::B8G8R8A8UIntNormalized// HDR 格式DirectXPixelFormat::R16G16B16A16Float如果捕获 HDR 内容但指定 SDR 格式WGC 会在内部进行格式转换你拿到的帧永远是创建时指定的格式。D3D11 Device 创建详解D3D11CreateDevice 函数原型HRESULTD3D11CreateDevice(IDXGIAdapter*pAdapter,// 显卡适配器nullptr 默认D3D_DRIVER_TYPE DriverType,// 驱动类型HMODULE Software,// 软件光栅化 DLL通常 nullptrUINT Flags,// 创建标志constD3D_FEATURE_LEVEL*pFeatureLevels,// Feature Level 列表UINT FeatureLevels,// Feature Level 数量UINT SDKVersion,// SDK 版本固定 D3D11_SDK_VERSIONID3D11Device**ppDevice,// 输出设备D3D_FEATURE_LEVEL*pFeatureLevel,// 输出实际 Feature LevelID3D11DeviceContext**ppImmediateContext// 输出上下文);驱动类型枚举值说明D3D_DRIVER_TYPE_HARDWARE使用 GPU 硬件加速正常使用D3D_DRIVER_TYPE_WARPCPU 模拟 GPU无 GPU 时保证可用D3D_DRIVER_TYPE_REFERENCE官方参考驱动调试用极慢创建标志Flag作用D3D11_CREATE_DEVICE_BGRA_SUPPORT支持 BGRA 格式WGC 必须开启D3D11_CREATE_DEVICE_VIDEO_SUPPORT启用视频处理能力颜色转换、缩放等D3D11_CREATE_DEVICE_DEBUG启用 D3D 调试层推荐组合autoflagsD3D11_CREATE_DEVICE_BGRA_SUPPORT|D3D11_CREATE_DEVICE_VIDEO_SUPPORT;示例代码winrt::com_ptrID3D11DeviceCreateD3D11Device(){winrt::com_ptrID3D11Devicedevice;UINT flagsD3D11_CREATE_DEVICE_BGRA_SUPPORT;// 优先使用硬件加速HRESULT hrD3D11CreateDevice(nullptr,D3D_DRIVER_TYPE_HARDWARE,nullptr,flags,nullptr,0,D3D11_SDK_VERSION,device.put(),nullptr,nullptr);// 回退到 WARPif(hrDXGI_ERROR_UNSUPPORTED){hrD3D11CreateDevice(nullptr,D3D_DRIVER_TYPE_WARP,nullptr,flags,nullptr,0,D3D11_SDK_VERSION,device.put(),nullptr,nullptr);}winrt::check_hresult(hr);returndevice;}GraphicsCapturePickerGraphicsCapturePicker是 Windows 提供的系统 UI 选择器让用户选择要捕获的窗口或显示器。类似于文件选择器但它是窗口/屏幕选择器。注意事项需要 UI 线程和窗口句柄不适合纯后台库使用可考虑自行实现窗口枚举和选择 UI项目依赖配置NuGet 包packages.configpackages!-- C WinRT 支持 --packageidMicrosoft.Windows.CppWinRTversion2.0.240405.15/!-- 安全 C 工具库 (WIL) --packageidMicrosoft.Windows.ImplementationLibraryversion1.0.240803.1/!-- Windows SDK --packageidMicrosoft.Windows.SDK.CPPversion10.0.26100.1/packageidMicrosoft.Windows.SDK.CPP.x64version10.0.26100.1//packages必需的链接库windowsapp.lib // WinRT API 支持 d3d11.lib // Direct3D 11 dxgi.lib // DXGI实际踩坑经验1. 链接错误无法解析的外部符号 SetRestrictedErrorInfo原因缺少 WinRT 链接库解决方案在附加依赖库中添加windowsapp.lib2. 预编译头注意事项如果项目使用了预编译头pch.h确保所有 .cpp 文件在最前面 include pch.h或者项目设置中正确配置了预编译头选项3. 窗口大小变化处理捕获过程中窗口大小改变时旧的 FramePool 需要关闭使用新尺寸重新创建 FramePool重新创建 CaptureSession4. 内存管理每个帧用完后及时释放使用 RAII 智能指针管理资源避免在回调中分配大量内存5. 线程安全Create() 方式的回调在 UI 线程CreateFreeThreaded() 回调在工作线程注意多线程访问共享资源时的同步参考资料微软官方文档 - 屏幕捕获官方示例 - ScreenCaptureforHWNDWin32CaptureSample