最可靠截指定窗口内容的方法是用 getdc(hwnd) + bitblt,需确保窗口可见未最小化、正确处理 dpi 缩放、避免 gdi 句柄泄漏,并在必要时降级使用 printwindow 或 dxgi。

用 GetDC + BitBlt 截指定窗口内容最可靠
直接调 GetDC(NULL) 截全屏容易被 DWM 或高 DPI 缩放干扰,而对特定窗口用 GetDC(hWnd) 拿到其设备上下文再 BitBlt,能避开大部分合成层问题。关键前提是窗口必须可见且未最小化(IsWindowVisible(hWnd) 要为真),否则截出来是黑图或旧缓存。
常见错误:传入已销毁的 hWnd 导致 GetDC 返回 NULL;或没调 ReleaseDC 造成 GDI 句柄泄漏——Windows 下每个进程默认只有约 10000 个 GDI 句柄,泄漏后程序可能突然无法绘图。
- 先用
FindWindow或EnumWindows找到目标窗口句柄,注意类名/标题要完全匹配(区分大小写、空格) -
CreateCompatibleDC和CreateCompatibleBitmap必须基于目标窗口 DC 创建,不能用屏幕 DC - 截图前建议调
ShowWindow(hWnd, SW_RESTORE)确保窗口处于可绘制状态,但不要强制激活(避免打断用户操作)
处理高 DPI 缩放时必须用 GetDpiForWindow 校正尺寸
Win10+ 默认开启 DPI 缩放,GetWindowRect 返回的是逻辑坐标,直接当像素尺寸用会导致截图变小或裁剪。正确做法是先调 GetDpiForWindow(hWnd),再用 GetSystemMetricsForDpi(SM_CXFRAME, dpi) 等接口算真实像素宽高。
如果忽略 DPI,截图在 125% 缩放下会只有实际大小的 80%,且边缘常有 1px 黑边——这是因为窗口边框宽度等系统度量值没按 DPI 换算。
- 获取窗口客户区尺寸应改用
GetClientRect+AdjustWindowRectExForDpi,而非仅靠GetWindowRect -
BitBlt的宽高参数必须是物理像素值,不是RECT里原始数值 - 若需保存为 PNG,GDI+ 的
Gdiplus::Bitmap构造函数第三个参数(PixelFormat32bppARGB)必须显式指定,否则 Alpha 通道可能错乱
后台窗口(如最小化或被遮挡)只能靠 PrintWindow 强制渲染
PrintWindow 是 Windows 提供的“绕过可见性检查”机制,它向目标窗口发送 WM_PRINT 消息,让窗口自己绘制到指定 DC。但它不保证成功:部分程序(如 Chrome、Electron 应用)会忽略该消息,返回 FALSE;UWP 应用完全不支持。
调用前必须确保目标窗口允许接收 WM_PRINT(多数传统 Win32 程序支持),且传入的 DC 必须是兼容 DC(CreateCompatibleDC 创建),不能是屏幕 DC。
- 失败时
PrintWindow返回 0,此时只能降级为截取窗口所在屏幕区域(风险:可能包含其他窗口内容) - 某些游戏或全屏应用会禁用
WM_PRINT,此时无通用方案,需依赖 DirectX 截帧(如 DXGI Desktop Duplication API) -
PrintWindow速度比BitBlt慢 3–5 倍,频繁调用会导致目标窗口卡顿,建议加 100ms 间隔
保存为文件时优先选 Gdiplus::Bitmap::Save 而非 StretchDIBits
手动拼 DIB 头 + 位图数据容易出错:RGB 排列顺序(BGR)、行字节对齐(4 字节边界)、BI_RGB 与 BI_BITFIELDS 区分……稍错就生成损坏文件。GDI+ 的 Save 方法内部已处理所有编码细节,且支持 PNG/JPEG/WebP。
但要注意初始化 GDI+:必须在主线程调 Gdiplus::GdiplusStartup,且每个进程只需一次;忘记初始化会导致 Save 返回 GenericError。
- 保存路径含中文或特殊字符时,
Save的第二个参数要用MultiByteToWideChar转 UTF-16,不能直接传 ANSI 字符串 - PNG 透明背景需确保 Bitmap 使用
PixelFormat32bppARGB,并预先用Graphics::Clear(Color::Transparent) - 若需高性能批量截图,避免每帧都新建
Gdiplus::Bitmap,复用对象并调SetResolution重置尺寸
PrintWindow 都静默失败。这时候只能提前检测 GetWindowLong(hWnd, GWL_EXSTYLE) & WS_EX_LAYERED 是否为真,或者尝试读取 GetProp(hWnd, L"__COMPAT_LAYER"),但这些都不是 100% 可靠的信号。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











