最直接方式是调用Graphics.CopyFromScreen,需传入屏幕绝对坐标与尺寸,注意多屏、DPI缩放及位图像素格式(应为Format32bppPArgb)以确保截图准确和透明通道不丢失。

怎么用 Graphics.CopyFromScreen 截指定区域
最直接的方式是调用 Graphics.CopyFromScreen,它能按坐标+宽高从屏幕拷贝像素到内存位图。但要注意:它只支持主屏原点(0,0)为左上角的全局坐标系,如果你拖动过显示器或用了多屏缩放,传进去的 x/y 必须是屏幕绝对坐标,不是窗体客户区坐标。
常见错误是直接用鼠标在窗体里获取的 Point 去截——结果截到黑块或错位。得先用 Cursor.Position 拿屏幕坐标,再确认是否落在有效显示器内:
-
Cursor.Position返回的是整个虚拟屏幕的绝对坐标,可能超出单个显示器范围 - 用
Screen.FromPoint判断该点属于哪块屏,再用screen.Bounds做裁剪,避免传负宽高导致异常 - 目标
Bitmap创建时尺寸必须和要截的区域一致,否则图像拉伸或截断
示例片段:
var pt = Cursor.Position;
var screen = Screen.FromPoint(pt);
var rect = new Rectangle(
Math.Max(screen.Bounds.Left, pt.X - 100),
Math.Max(screen.Bounds.Top, pt.Y - 100),
200, 200
);
using var bmp = new Bitmap(200, 200);
using var g = Graphics.FromImage(bmp);
g.CopyFromScreen(rect.Location, Point.Empty, rect.Size);为什么 PrintWindow 比 CopyFromScreen 更可靠
CopyFromScreen 截不到某些全屏独占渲染的窗口(比如游戏、部分视频播放器),也截不到带硬件加速的浏览器标签页内容;而 PrintWindow 是 Windows API 提供的“请求窗口自己画给自己”,能拿到更准确的 UI 快照,尤其适合截图含透明/动画/自绘控件的界面。
但它不是万能的:需要 P/Invoke 调用,且对无句柄的 UWP 应用或沙盒进程(如 Edge 的 renderer 进程)无效;另外,目标窗口必须处于可响应状态,最小化时可能返回空白。
- 必须用
FindWindow或EnumWindows先拿到目标窗口句柄(IntPtr) -
PrintWindow的flags参数传0表示正常绘制,传1(PW_CLIENTONLY)会跳过非客户区(标题栏、边框) - 目标
Bitmap的Graphics必须用Graphics.FromHdc绑定设备上下文,不能用FromImage
双屏+缩放下坐标换算容易出错的地方
Windows 多显示器 + 不同 DPI 缩放(比如主屏 125%,副屏 100%)会让坐标系统变得混乱。Cursor.Position 返回的是物理像素坐标,而 Graphics.CopyFromScreen 期望的却是“逻辑像素”坐标——如果不做 DPI 适配,截图会偏移或模糊。
- .NET 5+ 默认启用 Per-Monitor DPI 感知,但 WinForms 项目需在
app.manifest中显式设置highDpiAwareness="PerMonitorV2" - 用
Graphics.DpiX/DpiY获取当前屏幕 DPI,再用VisualTreeHelper.GetDpi(WPF)或GetDpiForWindow(Win32)校准鼠标坐标 - 简单绕过法:不用
CopyFromScreen,改用Graphics.CopyFromScreen的替代方案——先用Graphics.CopyFromScreen截整屏,再用Bitmap.Clone裁剪,这样坐标始终在位图内部,避开 DPI 换算
截图保存为 PNG 时透明通道丢失怎么办
默认用 Bitmap.Save(..., ImageFormat.Png) 保存,如果源图没启用 Alpha 通道,哪怕截图区域有半透明窗口(如 QQ 气泡、毛玻璃效果),保存后也会变成纯白背景。根本原因是 Bitmap 构造时没指定像素格式。
- 创建位图必须用
new Bitmap(width, height, PixelFormat.Format32bppPArgb),PArgb表示预乘 Alpha,Windows GDI+ 对它支持最稳 - 后续所有
Graphics操作(包括CopyFromScreen)都必须基于这个位图,不能中途转成Format24bppRgb - 保存前检查
bmp.PixelFormat是否为Format32bppPArgb,否则 PNG 会丢透明信息,看起来像“截图发灰”
事情说清了就结束。











