C#怎么用Graphics类实现全屏与区域屏幕截图?

来源:菜鸟站长作者:梧桐头衔:草根站长
导读:本期聚焦于小伙伴创作的《C#怎么用Graphics类实现全屏与区域屏幕截图?》,敬请观看详情。想把整个屏幕或者其中一块区域保存成图片,用C#自带的Graphics类就能搞定,不需要第三方库。核心思路是先通过Screen类拿到屏幕的宽高,再用Bitmap创建画布,接着从Graphics.FromImage取出绘图对象,最后调用CopyFromScreen把屏幕像素搬进画布。区域抓图只是把起点坐标和宽高换成用户框选的数值。实际写的时候要注意设备上下文释放和DPI缩放,否则高分屏上截出来的图会偏小。下面直接给出可运行的代码和参数说明,帮你少走弯路。

在C#中利用Graphics类完成屏幕截图,本质上是把屏幕设备上下文中的像素复制到内存中的Bitmap画布上。系统提供的System.Drawing命名空间已经封装了足够底层的API,开发者只需组合Screen、Bitmap、Graphics三个类型即可实现全屏或指定区域抓图,整个过程不依赖任何外部组件。

C#怎么用Graphics类实现全屏与区域屏幕截图?

一、全屏截图的基本实现

全屏截图的逻辑非常直观:首先获取主屏幕的边界尺寸,然后创建一个同样大小的Bitmap对象作为目标画布,通过Graphics.FromImage方法得到与该画布关联的绘图表面,最后调用CopyFromScreen把屏幕左上角(0,0)开始的全部像素拷贝过来。这种写法在单显示器环境下完全够用,而且资源占用极低。

需要注意,Graphics和Bitmap都实现了IDisposable接口,必须使用using语句或者手动调用Dispose,否则频繁截图会造成GDI对象泄漏。下面的示例演示了最基础的全屏保存为PNG文件的做法,路径部分可按需修改。

using System;
using System.Drawing;
using System.Drawing.Imaging;

class ScreenShotDemo
{
    static void CaptureFullScreen()
    {
        // 获取主屏幕的宽高
        int width = Screen.PrimaryScreen.Bounds.Width;
        int height = Screen.PrimaryScreen.Bounds.Height;

        // 创建内存画布
        using (Bitmap bmp = new Bitmap(width, height))
        {
            using (Graphics g = Graphics.FromImage(bmp))
            {
                // 从屏幕(0,0)拷贝到画布(0,0),尺寸为width x height
                g.CopyFromScreen(0, 0, 0, 0, new Size(width, height));
            }
            // 保存为文件
            bmp.Save("fullscreen.png", ImageFormat.Png);
        }
    }
}

1.1 关于Screen类的选择

上面的代码只使用了PrimaryScreen,也就是主显示器。如果用户接了双屏,可能需要遍历Screen.AllScreens来合并所有屏幕区域。每个Screen对象的Bounds属性都带有X、Y偏移量,拼接多屏位图时要按偏移绘制,不能直接从(0,0)拷贝。

另外,Screen类位于System.Windows.Forms程序集中,如果是控制台项目需要手动引用该程序集并添加using System.Windows.Forms;,否则编译时会找不到类型。这也是很多新手第一次写截图代码报错的主要原因。

二、区域截图的实现方式

区域截图和全屏截图的区别仅在于参数:不再从(0,0)取整个屏幕,而是取用户指定的起点坐标以及宽高。常见做法是提供一个Rectangle结构,里面包含X、Y、Width、Height,然后把这个矩形传给CopyFromScreen的源点和目标点。比如想截取屏幕上从(100,100)开始、宽300高200的一块,就写成g.CopyFromScreen(100, 100, 0, 0, new Size(300, 200))。

为了让用户可视化选择区域,通常会配合Mouse事件在窗体上拖出虚线框,再把框的矩形传进截图方法。下面给出一个简化版的区域截图函数,入参是Rectangle,返回Bitmap,调用方自行决定保存还是显示。

using System;
using System.Drawing;

class RegionCapture
{
    static Bitmap CaptureRectangle(Rectangle rect)
    {
        if (rect.Width <= 0 || rect.Height <= 0)
            throw new ArgumentException("截取区域宽高必须大于0");

        Bitmap bmp = new Bitmap(rect.Width, rect.Height);
        using (Graphics g = Graphics.FromImage(bmp))
        {
            // 源起点为rect.X, rect.Y,目标起点为0,0
            g.CopyFromScreen(rect.X, rect.Y, 0, 0, rect.Size);
        }
        return bmp;
    }
}

2.1 坐标与DPI缩放问题

在Windows高DPI设置下,窗体如果使用感知模式(Per Monitor DPI Aware),鼠标得到的客户坐标和屏幕物理像素并不一致。这时直接把窗体坐标传给CopyFromScreen会导致截偏或截小。解决办法是在截图前把逻辑坐标乘以当前屏幕的DPI缩放比,或者把应用程序清单设为不感知DPI,由系统自动拉伸。

还有一个隐蔽坑:CopyFromScreen使用的是屏幕设备坐标,而不是窗体客户区坐标。如果区域框是在某个子控件上画的,必须用PointToScreen把局部坐标转换成屏幕坐标,否则截出来的图会是错位的。

三、完整可运行的控制台示例

下面把全屏和区域两种能力合并成一个简单的控制台程序,通过命令行参数决定模式。虽然控制台没有界面,但足以验证Graphics截图的可用性,你也可以把方法搬到WinForms项目里做界面化工具。

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;

class Program
{
    static void Main(string[] args)
    {
        if (args.Length == 0)
        {
            // 默认全屏
            CaptureFull();
        }
        else if (args.Length == 4)
        {
            // 传入 x y width height 做区域
            int x = int.Parse(args[0]);
            int y = int.Parse(args[1]);
            int w = int.Parse(args[2]);
            int h = int.Parse(args[3]);
            CaptureRegion(new Rectangle(x, y, w, h));
        }
    }

    static void CaptureFull()
    {
        Rectangle bounds = Screen.PrimaryScreen.Bounds;
        using (Bitmap bmp = new Bitmap(bounds.Width, bounds.Height))
        {
            using (Graphics g = Graphics.FromImage(bmp))
            {
                g.CopyFromScreen(bounds.X, bounds.Y, 0, 0, bounds.Size);
            }
            bmp.Save("full.png", ImageFormat.Png);
            Console.WriteLine("全屏截图已保存");
        }
    }

    static void CaptureRegion(Rectangle rect)
    {
        using (Bitmap bmp = new Bitmap(rect.Width, rect.Height))
        {
            using (Graphics g = Graphics.FromImage(bmp))
            {
                g.CopyFromScreen(rect.X, rect.Y, 0, 0, rect.Size);
            }
            bmp.Save("region.png", ImageFormat.Png);
            Console.WriteLine("区域截图已保存");
        }
    }
}

3.1 性能与格式选择

截图本身耗时极短,瓶颈通常在保存阶段。PNG无损但体积大,JPEG体积小但丢失文字清晰度。如果是做屏幕录制类工具,建议用Bitmap直接扔进内存流再编码,不要每次都落盘。此外,CopyFromScreen内部调用了BitBlt,在显卡驱动异常时可能返回空白图,生产环境应加try-catch并提示用户。

Graphics类方案胜在零依赖、代码少,但在截取DirectX全屏游戏画面时往往只能得到黑屏,因为游戏使用的是独立渲染表面。这种场景要改用DirectShow或桌面 duplication API,已经超出Graphics类的能力范围。

四、小结与避坑清单

用C#的Graphics做截图,核心就是Bitmap加CopyFromScreen,全屏和区域只是参数不同。真正写进项目时,记得处理多屏边界、DPI缩放、资源释放三件事,基本就不会出大问题。

  • 始终用using包裹Bitmap和Graphics,防止GDI泄漏
  • 区域坐标必须是屏幕绝对坐标,不是控件本地坐标
  • 高DPI下自行换算缩放比,或关闭DPI感知
  • 需要截游戏画面时放弃Graphics,换系统级捕获接口

只要理清上面几点,你就能在十行内写出一个实用的C#截屏模块,后续接上鼠标框选或者快捷键监听,就是一个完整的桌面抓图小工具。

C#Graphicsscreen_capture修改时间:2026-08-09 15:42:41

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。