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

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

yuyutoo 2024-11-26 10:37 2 浏览 0 评论

前言

在桌面软件开发中,屏幕截图功能已经成为许多应用程序的重要组成部分。无论是记录用户操作、创建文档、捕捉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#

相关推荐

TCP协议原理,有这一篇就够了

先亮出这篇文章的思维导图:TCP作为传输层的协议,是一个软件工程师素养的体现,也是面试中经常被问到的知识点。在此,我将TCP核心的一些问题梳理了一下,希望能帮到各位。001.能不能说一说TC...

Win10专业版无线网络老是掉线的问题

有一位电脑基地的用户,使用...

学习计算机网络需要掌握以下几方面基础知识

计算机基础知识操作系统:了解常见操作系统(如Windows、Linux)的基本操作和网络配置,例如如何设置IP地址、子网掩码、网关和DNS服务器等,以及如何通过命令行工具(如ping、tr...

网络工程师的圣经!世界级网工手绘268张图让TCP/IP直接通俗易懂

要把知识通俗地讲明白,真的不容易。——读者说TCP/IP从字面意义上讲,有人可能会认为TCP/IP是指TCP和IP两种协议。实际生活当中有时候也确实就是这两种协议。然而在很多情况下,它只是...

三分钟了解通信知识TCP与IP协议(含“通信技术”资料分享)

TCP/IPTCP/IP分层模型①应用层...

网闸与防火墙:网络安全设备的差异与应用

在网络安全领域,网闸(安全隔离网闸,GAP)和防火墙(Firewall)是两类重要的防护设备。尽管它们都服务于网络安全防护,但在设计理念、技术原理、安全效能及适用场景等方面存在显著差异,以下从五个维度...

S7-300的TCP/IP通信

一、首先在项目中创建2个S7-300的站点;二、硬件组态中,设置合适的TCP/IP地址,在同一网段内;...

西门子S7-1500 PLC的 MODBUS TCP通信

MODBUSTCP使MODBUS_RTU协议运行于以太网,MODBUSTCP使用TCP/IP和以太网在站点间传送MODBUS报文,MODBUSTCP结合了以太网物理网络和网络标准TC...

系统规划与管理师新版备考必备:第7章考点思维导图解析

备考系统规划与管理师的小伙伴们,福利又来啦!今天为大家带来《系统规划与管理师(第2版)》第7章考点的思维导图,助你高效梳理重点,让备考更有方向!...

TCP/IP、Http、Socket 有何区别与联系?

HTTP协议对应于应用层,Socket则是对TCP/IP协议的封装和应用(程序员层面上)。HTTP是应用层协议,主要解决如何包装数据。而我们平时说的最多的Socket是什么呢?实际上...

西门子PLC串口协议与以太网通信协议对比

西门子plc品牌众多,通信协议的类型就更多了,具体可分为串口协议和以太网通信协议两大类。...

网络编程懒人入门(十三):一泡尿的时间,快速搞懂TCP和UDP的区别

本文引用了作者Fundebug的“一文搞懂TCP与UDP的区别”一文的内容,感谢无私分享。1、引言...

程序员必备的学习笔记《TCP/IP详解(一)》

为什么会有TCP/IP协议在世界上各地,各种各样的电脑运行着各自不同的操作系统为大家服务,这些电脑在表达同一种信息的时候所使用的方法是千差万别。就好像圣经中上帝打乱了各地人的口音,让他们无法合作一样...

一文读懂TCP/IP协议工作原理和工作流程

简述本文主要介绍TCP/IP协议工作原理和工作流程。含义TCP/IP协议,英文全称TransmissionControlProtocol/InternetProtocol,包含了一系列构成互联网...

如何在 Windows 10 和 Windows 11 上重置 TCP/IP 堆栈

传输控制协议/Internet协议,通常称为TCP/IP,是您的WindowsPC如何与Internet上的其他设备进行通信的关键部分。但是当事情出错时会发生什么?你如何解决它?幸运的...

取消回复欢迎 发表评论: