百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

深入解析C#屏幕截图技术:实现多种实用截图方法

bigegpt 2024-10-15 08:39 4 浏览

前言

在桌面软件开发中,屏幕截图功能已经成为许多应用程序的重要组成部分。无论是记录用户操作、创建文档、捕捉Bug,还是制作演示材料,能够灵活地截取屏幕或特定区域的图像都是极其有用的。然而,实现这一功能并不是一件简单的事情,需要深入了解系统的底层API。

如果你曾经想要为你的C#应用程序添加屏幕截图功能,那么你来对地方了!在这篇文章中,我们将揭示如何使用C#和Windows API来实现多种实用的截图方法。你将学习如何截取整个屏幕、特定控件的区域以及WinForm控件的内容。无论你是初学者还是有经验的开发者,这些技巧都将大大提升你在图像处理方面的能力。准备好了吗?让我们一起深入探讨C#的屏幕截图技术吧!

介绍

在本文中,我们将编写一个ScreenshotHelper的类,它通过调用Windows API提供了强大的屏幕截图功能。这个类可以帮助我们实现以下功能:

  1. 截取整个屏幕
  2. 截取特定控件的区域
  3. 从WinForm控件内容区域获取截图

实现步骤

1. 引入必要的命名空间和API函数

首先,我们需要引入一些命名空间并定义调用Windows API函数所需的类。GDI32 和 User32 辅助类这两个类分别封装了GDI32和User32的API函数,提供了截图所需的底层功能。

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Windows;

namespace ScreenRecord
{
	public class GDI32
	{
	    // SRCCOPY 是位块传输操作的光栅操作代码,表示直接从源区域复制到目标区域。
	    public const int SRCCOPY = 0x00CC0020;
	
	    // BitBlt 函数执行位块传输操作,将指定源设备上下文的颜色数据复制到目标设备上下文中。
	    [DllImport("gdi32.dll")]
	    public static extern bool BitBlt(
	        IntPtr hObject,      // 目标设备上下文的句柄。
	        int nXDest,          // 目标矩形的左上角 x 坐标。
	        int nYDest,          // 目标矩形的左上角 y 坐标。
	        int nWidth,          // 源和目标矩形的宽度。
	        int nHeight,         // 源和目标矩形的高度。
	        IntPtr hObjectSource,// 源设备上下文的句柄。
	        int nXSrc,           // 源矩形的左上角 x 坐标。
	        int nYSrc,           // 源矩形的左上角 y 坐标。
	        int dwRop            // 光栅操作代码,定义如何组合源位图和目标位图的颜色数据以达到最终颜色。
	    );
	
	    // CreateCompatibleBitmap 函数创建一个与指定设备上下文兼容的位图。
	    [DllImport("gdi32.dll")]
	    public static extern IntPtr CreateCompatibleBitmap(
	        IntPtr hDC,  // 设备上下文的句柄。
	        int nWidth,  // 位图的宽度(像素)。
	        int nHeight  // 位图的高度(像素)。
	    );
	
	    // CreateCompatibleDC 函数创建一个与指定设备上下文兼容的内存设备上下文(DC)。
	    [DllImport("gdi32.dll")]
	    public static extern IntPtr CreateCompatibleDC(
	        IntPtr hDC   // 现有设备上下文的句柄。
	    );
	
	    // DeleteDC 函数删除指定的设备上下文(DC)。
	    [DllImport("gdi32.dll")]
	    public static extern bool DeleteDC(
	        IntPtr hDC   // 设备上下文的句柄。
	    );
	
	    // DeleteObject 函数删除逻辑笔、刷子、字体、位图、区域或调色板,释放与对象关联的所有系统资源。
	    [DllImport("gdi32.dll")]
	    public static extern bool DeleteObject(
	        IntPtr hObject  // 逻辑对象的句柄。
	    );
	
	    // SelectObject 函数将一个对象选入指定的设备上下文(DC)。新对象将替换前一个相同类型的对象。
	    [DllImport("gdi32.dll")]
	    public static extern IntPtr SelectObject(
	        IntPtr hDC,      // 设备上下文的句柄。
	        IntPtr hObject   // 要选入的对象的句柄。
	    );
	}
}
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Windows;

namespace ScreenRecord
{
	public class User32
	{
	    // 定义一个表示矩形结构体,用于保存窗口的坐标信息。
	    [StructLayout(LayoutKind.Sequential)]
	    public struct RECT
	    {
	        public int left;    // 矩形左上角的 x 坐标。
	        public int top;     // 矩形左上角的 y 坐标。
	        public int right;   // 矩形右下角的 x 坐标。
	        public int bottom;  // 矩形右下角的 y 坐标。
	    }
	
	    // 获取桌面窗口的句柄。
	    [DllImport("user32.dll")]
	    public static extern IntPtr GetDesktopWindow();
	
	    // 获取指定窗口的设备上下文(DC)。
	    [DllImport("user32.dll")]
	    public static extern IntPtr GetWindowDC(IntPtr hWnd);
	
	    // 释放指定窗口的设备上下文(DC)。
	    [DllImport("user32.dll")]
	    public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDC);
	
	    // 获取指定窗口的客户区域或者整个窗口的矩形坐标。
	    [DllImport("user32.dll")]
	    public static extern IntPtr GetWindowRect(IntPtr hWnd, ref RECT rect);
	}
}

2. 截取整个屏幕

我们先实现一个方法,用于截取整个屏幕。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;
using System.Windows;

namespace ScreenRecord
{
   public static class ScreenshotHelper
   {
   	 public static Bitmap GetImageFromWholeScreen()
   	 {
       System.Windows.Size size = new System.Windows.Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
       System.Windows.Point point = new System.Windows.Point(0, 0);
       return Screenshot(point, size);
   	}
   }
}

3. 截取特定控件的区域

接下来,实现从屏幕获取控件范围截图的方法。

public static Bitmap GetControlImageFromScreen(FrameworkElement cc)
 {
     System.Windows.Window window = System.Windows.Window.GetWindow(cc);
     System.Windows.Point point = window == null ? cc.PointToScreen(new System.Windows.Point(0, 0)) : cc.TransformToAncestor(window).Transform(new System.Windows.Point(0, 0));
     System.Windows.Size size = new System.Windows.Size((int)cc.ActualWidth, (int)cc.ActualHeight);
     return Screenshot(point, size);
 }

4. 从WinForm控件内容获取截图

最后,实现从WinForm控件区域内容获取截图的方法。

 public static Bitmap GetControlImageFromWinFormControl(IntPtr handle)
  {
      IntPtr hdcSrc = User32.GetWindowDC(handle);
      User32.RECT windowRect = new User32.RECT();
      User32.GetWindowRect(handle, ref windowRect);
      int width = windowRect.right - windowRect.left;
      int height = windowRect.bottom - windowRect.top;
      IntPtr hdcDest = GDI32.CreateCompatibleDC(hdcSrc);
      IntPtr hBitmap = GDI32.CreateCompatibleBitmap(hdcSrc, width, height);
      IntPtr hOld = GDI32.SelectObject(hdcDest, hBitmap);
      GDI32.BitBlt(hdcDest, 0, 0, width, height, hdcSrc, 0, 0, GDI32.SRCCOPY);
      GDI32.SelectObject(hdcDest, hOld);
      GDI32.DeleteDC(hdcDest);
      User32.ReleaseDC(handle, hdcSrc);
      Bitmap img = Bitmap.FromHbitmap(hBitmap);
      GDI32.DeleteObject(hBitmap);
      return img;
  }

5. 截图实现方法

最后,我们实现截图的核心方法。

 public static Bitmap Screenshot(System.Windows.Point startPoint, System.Windows.Size rangeSize)
 {
     Rectangle rc = new Rectangle()
     {
         X = (int)startPoint.X,
         Y = (int)startPoint.Y,
         Width = (int)rangeSize.Width,
         Height = (int)rangeSize.Height
     };	// 获取屏幕的宽和高
     Bitmap bitmap = new Bitmap(rc.Width, rc.Height);
     using (Graphics g = Graphics.FromImage(bitmap))
     {
         // 拷贝屏幕
         g.CopyFromScreen(rc.X, rc.Y, 0, 0, rc.Size, CopyPixelOperation.SourceCopy);
     }
     return bitmap;
 }

6、测试项目

新建一个ScreenRecordApp的winform项目和ScreenRecord的类库。如图所示


编写测试代码:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        var bitmap = ScreenshotHelper.GetControlImageFromWinFormControl(this.panel1.Handle);
        bitmap.Save("1.png");
        bitmap.Dispose();
        MessageBox.Show("截图成功!");
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        this.pictureBox1.Load("https://ts1.cn.mm.bing.net/th/id/R-C.9e45a633e95179a37c907fa2797999ad?rik=aMuPS4TunAh5ZA&riu=http%3a%2f%2fwww.quazero.com%2fuploads%2fallimg%2f140303%2f1-140303214Q2.jpg&ehk=P%2firfYpARc1fHht%2bWpapYR4W15p6SLABE8CBexoeon4%3d&risl=&pid=ImgRaw&r=0");
    }
}

使用panel容器加载一个PictureBox控件,PictureBox随意加载一副网络图片。然后,我们使用ScreenshotHelper.GetControlImageFromWinFormControl截图方法,只需要传入panel控件的句柄,內部可以计算得到图片的大小,再对返回的bitmap保存到指定的文件。运行成功如图所示:

查看本地路径的图片:

其他的方式,调用方式也类似,传递的控件句柄。后续还可以扩展自定义区域选择。

总结

通过以上步骤,我们实现了一个功能强大的C#屏幕截图工具类 ScreenshotHelper。这个类利用Windows API,提供了截取整个屏幕、特定控件区域和WinForm控件内容的多种实用截图方法。希望本文对你理解和实现屏幕截图功能有所帮助。

如果本文对你有帮助,我将非常荣幸。

如果你对C#截图有其他的用法,欢迎留言交流。

如果你喜欢我的文章,谢谢三连,点赞,关注,转发吧!!!

#头条首发必备指南# #头条创作挑战赛# #记录我的2024#

相关推荐

得物可观测平台架构升级:基于GreptimeDB的全新监控体系实践

一、摘要在前端可观测分析场景中,需要实时观测并处理多地、多环境的运行情况,以保障Web应用和移动端的可用性与性能。传统方案往往依赖代理Agent→消息队列→流计算引擎→OLAP存储...

warm-flow新春版:网关直连和流程图重构

本期主要解决了网关直连和流程图重构,可以自此之后可支持各种复杂的网关混合、多网关直连使用。-新增Ruoyi-Vue-Plus优秀开源集成案例更新日志[feat]导入、导出和保存等新增json格式支持...

扣子空间体验报告

在数字化时代,智能工具的应用正不断拓展到我们工作和生活的各个角落。从任务规划到项目执行,再到任务管理,作者深入探讨了这款工具在不同场景下的表现和潜力。通过具体的应用实例,文章展示了扣子空间如何帮助用户...

spider-flow:开源的可视化方式定义爬虫方案

spider-flow简介spider-flow是一个爬虫平台,以可视化推拽方式定义爬取流程,无需代码即可实现一个爬虫服务。spider-flow特性支持css选择器、正则提取支持JSON/XML格式...

solon-flow 你好世界!

solon-flow是一个基础级的流处理引擎(可用于业务规则、决策处理、计算编排、流程审批等......)。提供有“开放式”驱动定制支持,像jdbc有mysql或pgsql等驱动,可...

新一代开源爬虫平台:SpiderFlow

SpiderFlow:新一代爬虫平台,以图形化方式定义爬虫流程,不写代码即可完成爬虫。-精选真开源,释放新价值。概览Spider-Flow是一个开源的、面向所有用户的Web端爬虫构建平台,它使用Ja...

通过 SQL 训练机器学习模型的引擎

关注薪资待遇的同学应该知道,机器学习相关的岗位工资普遍偏高啊。同时随着各种通用机器学习框架的出现,机器学习的门槛也在逐渐降低,训练一个简单的机器学习模型变得不那么难。但是不得不承认对于一些数据相关的工...

鼠须管输入法rime for Mac

鼠须管输入法forMac是一款十分新颖的跨平台输入法软件,全名是中州韵输入法引擎,鼠须管输入法mac版不仅仅是一个输入法,而是一个输入法算法框架。Rime的基础架构十分精良,一套算法支持了拼音、...

Go语言 1.20 版本正式发布:新版详细介绍

Go1.20简介最新的Go版本1.20在Go1.19发布六个月后发布。它的大部分更改都在工具链、运行时和库的实现中。一如既往,该版本保持了Go1的兼容性承诺。我们期望几乎所...

iOS 10平台SpriteKit新特性之Tile Maps(上)

简介苹果公司在WWDC2016大会上向人们展示了一大批新的好东西。其中之一就是SpriteKitTileEditor。这款工具易于上手,而且看起来速度特别快。在本教程中,你将了解关于TileE...

程序员简历例句—范例Java、Python、C++模板

个人简介通用简介:有良好的代码风格,通过添加注释提高代码可读性,注重代码质量,研读过XXX,XXX等多个开源项目源码从而学习增强代码的健壮性与扩展性。具备良好的代码编程习惯及文档编写能力,参与多个高...

Telerik UI for iOS Q3 2015正式发布

近日,TelerikUIforiOS正式发布了Q32015。新版本新增对XCode7、Swift2.0和iOS9的支持,同时还新增了对数轴、不连续的日期时间轴等;改进TKDataPoin...

ios使用ijkplayer+nginx进行视频直播

上两节,我们讲到使用nginx和ngixn的rtmp模块搭建直播的服务器,接着我们讲解了在Android使用ijkplayer来作为我们的视频直播播放器,整个过程中,需要注意的就是ijlplayer编...

IOS技术分享|iOS快速生成开发文档(一)

前言对于开发人员而言,文档的作用不言而喻。文档不仅可以提高软件开发效率,还能便于以后的软件开发、使用和维护。本文主要讲述Objective-C快速生成开发文档工具appledoc。简介apple...

macOS下配置VS Code C++开发环境

本文介绍在苹果macOS操作系统下,配置VisualStudioCode的C/C++开发环境的过程,本环境使用Clang/LLVM编译器和调试器。一、前置条件本文默认前置条件是,您的开发设备已...