深入解析C#屏幕截图技术:实现多种实用截图方法
bigegpt 2024-10-15 08:39 4 浏览
前言
在桌面软件开发中,屏幕截图功能已经成为许多应用程序的重要组成部分。无论是记录用户操作、创建文档、捕捉Bug,还是制作演示材料,能够灵活地截取屏幕或特定区域的图像都是极其有用的。然而,实现这一功能并不是一件简单的事情,需要深入了解系统的底层API。
如果你曾经想要为你的C#应用程序添加屏幕截图功能,那么你来对地方了!在这篇文章中,我们将揭示如何使用C#和Windows API来实现多种实用的截图方法。你将学习如何截取整个屏幕、特定控件的区域以及WinForm控件的内容。无论你是初学者还是有经验的开发者,这些技巧都将大大提升你在图像处理方面的能力。准备好了吗?让我们一起深入探讨C#的屏幕截图技术吧!
介绍
在本文中,我们将编写一个ScreenshotHelper的类,它通过调用Windows API提供了强大的屏幕截图功能。这个类可以帮助我们实现以下功能:
- 截取整个屏幕
- 截取特定控件的区域
- 从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#截图有其他的用法,欢迎留言交流。
如果你喜欢我的文章,谢谢三连,点赞,关注,转发吧!!!
相关推荐
- 得物可观测平台架构升级:基于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编译器和调试器。一、前置条件本文默认前置条件是,您的开发设备已...
- 一周热门
- 最近发表
- 标签列表
-
- mybatiscollection (79)
- mqtt服务器 (88)
- keyerror (78)
- c#map (65)
- resize函数 (64)
- xftp6 (83)
- bt搜索 (75)
- c#var (76)
- mybatis大于等于 (64)
- xcode-select (66)
- httperror403.14-forbidden (63)
- logstashinput (65)
- hadoop端口 (65)
- dockernetworkconnect (63)
- esxi7 (63)
- vue阻止冒泡 (67)
- c#for循环 (63)
- oracle时间戳转换日期 (64)
- jquery跨域 (68)
- php写入文件 (73)
- java大写转小写 (63)
- kafkatools (66)
- mysql导出数据库 (66)
- jquery鼠标移入移出 (71)
- 取小数点后两位的函数 (73)