共计 2699 个字符,预计需要花费 7 分钟才能阅读完成。
背景痛点
在开发需要录屏功能的 C# 应用时,很多开发者首先想到的是第三方库,比如 OBS 的封装库或者 FFmpeg。这些方案虽然功能强大,但也存在一些明显的痛点:

- 依赖重:需要附带大量 DLL 文件或运行时环境,显著增加应用体积
- 授权复杂:部分库存在 GPL 协议风险,商业应用需谨慎处理
- 性能开销:多层抽象导致 CPU/ 内存占用较高
- 兼容性问题:不同 Windows 版本可能表现不一致
Windows 10 1809+ 自带的 Windows.Graphics.Capture API 恰好能解决这些问题,它作为系统原生组件,具有无额外依赖、硬件加速等优势。
技术方案对比
先横向对比几种常见录屏方案的特点:
- Windows.Graphics.Capture
- 优势:系统原生支持、硬件加速、内存占用低
-
局限:仅限 Win10 1809+、需处理 UWP 权限
-
DirectX 抓取
- 优势:极致性能、帧率可控性强
-
局限:开发复杂度高、需处理 DXGI 资源管理
-
FFmpeg+GDI
- 优势:跨平台、功能全面
- 局限: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();}
线程模型选择
根据场景选择合适线程模型:
- UI 密集型应用:使用 DispatcherQueueController 创建专用 UI 线程
- 后台服务:采用 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; // 禁用光标捕获
延伸开发方向
- 音频采集:结合 Windows.Media.Audio 捕获系统声音
- HDR 支持:检查 DXGI_OUTDUPL_METADATA_TYPE_HDR_METADATA
- 鼠标高亮效果:通过 Direct2D 实时绘制光标特效
官方资源
通过上述方案,我们实现了仅依赖系统组件的轻量级录屏方案。在实际项目中,建议结合 MediaFoundation 进行视频编码,可以达到更好的性能表现。
正文完
发表至: 编程开发
近两天内
