如何编程截取Zoom窗口?标准Windows API截图异常问题
如何正确截取Zoom窗口的视频内容?
我需要通过编程截取Zoom窗口,使用标准Windows API实现,但仅能捕获窗口框架,视频区域显示的是窗口后方的内容(测试时后方运行的是Visual Studio)。使用的代码如下:
Hdc hdcWindow = GetWindowDC(hwnd); Hdc hdcMemDC = CreateCompatibleDC(hdcWindow); var windowRect = new Rect(); GetWindowRect(hwnd, ref windowRect); int width = windowRect.Right - windowRect.Left; int height = windowRect.Bottom - windowRect.Top; var hbmScreen = CreateCompatibleBitmap(hdcWindow, width, height); var hOld = SelectObject(hdcMemDC, hbmScreen); // 尝试过的三种方式 BitBlt(hdcMemDC, 0, 0, width, height, hdcWindow, 0, 0, SRCCOPY); PrintWindow(hwnd, hdcMemDC, 0); // SendMessage(hwnd, WM_PRINT, hdcMemDC, (IntPtr)(PRF_CHILDREN | PRF_CLIENT | PRF_NONCLIENT)); // 获取位图 var bmp = System.Drawing.Bitmap.FromHbitmap(hbmScreen); // 资源清理 SelectObject(hdcMemDC, hOld); DeleteObject(hbmScreen); DeleteDC(hdcMemDC); ReleaseDC(hwnd, hdcWindow); return bmp;
实际运行后,Zoom的标题栏和控件能正常截取,但视频客户区始终显示窗口后方的内容,重启Zoom前每次截图结果一致。推测Zoom采用了非常规窗口机制,求正确的截取方法。
解决方案
Zoom的视频渲染采用了硬件加速的DirectComposition/Direct3D表面,这类内容无法被传统的GDI捕获API(BitBlt/PrintWindow/WM_PRINT)直接捕获,需要使用支持硬件加速内容的捕获方案:
1. 使用Windows.Graphics.Capture API(推荐,Windows 10 1809及以上)
这是微软官方提供的现代捕获API,专门针对硬件加速渲染的窗口/屏幕内容设计,步骤如下:
- 确保应用声明
graphicsCapture权限(UWP应用需在Package.appxmanifest中添加对应权限;Win32应用需通过SetProcessDpiAwarenessContext设置DPI感知,并启用相关权限) - 将Zoom窗口的
HWND转换为GraphicsCaptureItem - 创建捕获会话并监听帧到达事件,获取并处理捕获帧
示例代码:
using Windows.Graphics.Capture; using Windows.Graphics.DirectX; using Windows.Graphics.DirectX.Direct3D11; using System.Runtime.InteropServices; [ComImport] [Guid("3628E81B-3CAC-4C60-B7F4-23CE0E0C3356")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IGraphicsCaptureItemInterop { void CreateForWindow(IntPtr window, ref Guid iid, out GraphicsCaptureItem item); } // 将HWND转换为GraphicsCaptureItem public static async Task<GraphicsCaptureItem> GetCaptureItemFromHwnd(IntPtr hwnd) { var interop = GraphicsCaptureItem.As<IGraphicsCaptureItemInterop>(); Guid graphicsCaptureItemGuid = typeof(GraphicsCaptureItem).GUID; interop.CreateForWindow(hwnd, ref graphicsCaptureItemGuid, out var item); return item; } // 启动Zoom窗口捕获 public static async Task StartZoomCapture(IntPtr zoomHwnd) { var captureItem = await GetCaptureItemFromHwnd(zoomHwnd); var captureSession = GraphicsCaptureSession.Create(captureItem); // 可选:禁用光标捕获 captureSession.IsCursorCaptureEnabled = false; // 监听帧到达事件 captureSession.FrameArrived += (sender, args) => { using (var frame = sender.TryGetNextFrame()) { // 将Direct3D表面转换为可处理的图像格式 // 示例:转换为GDI Bitmap(需Direct3D11设备支持) var d3dSurface = frame.Surface; // 此处需实现Direct3D到GDI的像素转换逻辑 } }; captureSession.StartCapture(); }
2. 通过DwmGetWindowAttribute获取可视化表面
Zoom窗口的视频区域可能使用了DirectComposition渲染,可以通过DwmGetWindowAttribute获取窗口的可视化表面,再通过Direct3D API读取像素:
- 调用
DwmGetWindowAttribute,传入DWMWA_VISUALIZATION_ATTRIBUTES参数获取表面信息 - 使用Direct3D设备获取表面的像素数据
3. 临时方案:禁用Zoom硬件加速
如果允许修改Zoom设置,可在Zoom的「设置-视频」中关闭硬件加速选项,之后传统的PrintWindow即可正常捕获视频内容,但会降低Zoom的视频渲染性能。
内容的提问来源于stack exchange,提问作者Joshua Frank
相关产品推荐
相关产品推荐

