图像采集源除了显示控件(上一篇《.NET 控件转图片》有介绍从界面控件转图片),更多的是窗口以及屏幕。

窗口截图最常用的方法是GDI,直接上Demo吧:

 1         private void GdiCaptureButton_OnClick(object sender, RoutedEventArgs e)
2 {
3 var bitmap = CaptureScreen();
4 CaptureImage.Source = ConvertBitmapToBitmapSource(bitmap);
5 }
6 /// <summary>
7 /// 截图屏幕
8 /// </summary>
9 /// <returns></returns>
10 public static Bitmap CaptureScreen()
11 {
12 IntPtr desktopWindow = GetDesktopWindow();
13 //获取窗口位置大小
14 GetWindowRect(desktopWindow, out var lpRect);
15 return CaptureByGdi(desktopWindow, 0d, 0d, lpRect.Width, lpRect.Height);
16 }
17 private BitmapSource ConvertBitmapToBitmapSource(Bitmap bitmap)
18 {
19 using MemoryStream memoryStream = new MemoryStream();
20 // 将 System.Drawing.Bitmap 保存到内存流中
21 bitmap.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png);
22 // 重置内存流的指针到开头
23 memoryStream.Seek(0, SeekOrigin.Begin);
24
25 // 创建 BitmapImage 对象并从内存流中加载图像
26 BitmapImage bitmapImage = new BitmapImage();
27 bitmapImage.BeginInit();
28 bitmapImage.StreamSource = memoryStream;
29 bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
30 bitmapImage.EndInit();
31 // 确保内存流不会被回收
32 bitmapImage.Freeze();
33 return bitmapImage;
34 }
35 /// <summary>
36 /// 截图窗口/屏幕
37 /// </summary>
38 /// <param name="windowIntPtr">窗口句柄(窗口或者桌面)</param>
39 /// <param name="left">水平坐标</param>
40 /// <param name="top">竖直坐标</param>
41 /// <param name="width">宽度</param>
42 /// <param name="height">高度</param>
43 /// <returns></returns>
44 private static Bitmap CaptureByGdi(IntPtr windowIntPtr, double left, double top, double width, double height)
45 {
46 IntPtr windowDc = GetWindowDC(windowIntPtr);
47 IntPtr compatibleDc = CreateCompatibleDC(windowDc);
48 IntPtr compatibleBitmap = CreateCompatibleBitmap(windowDc, (int)width, (int)height);
49 IntPtr bitmapObj = SelectObject(compatibleDc, compatibleBitmap);
50 BitBlt(compatibleDc, 0, 0, (int)width, (int)height, windowDc, (int)left, (int)top, CopyPixelOperation.SourceCopy);
51 Bitmap bitmap = System.Drawing.Image.FromHbitmap(compatibleBitmap);
52 //释放
53 SelectObject(compatibleDc, bitmapObj);
54 DeleteObject(compatibleBitmap);
55 DeleteDC(compatibleDc);
56 ReleaseDC(windowIntPtr, windowDc);
57 return bitmap;
58 }

根据user32.dll下拿到的桌面信息-句柄获取桌面窗口的设备上下文,再以设备上下文分别创建内存设备上下文、设备位图句柄

BitBlt是最关键的函数,Gdi提供用于在设备上下文之间进行位图块的传输,从原设备上下文复现位图到创建的设备上下文

然后以设备位图句柄输出一个位图System.Drawing.Bitmap

使用到的User32、Gdi32函数:

  1     /// <summary>
2 /// 获取桌面窗口
3 /// </summary>
4 /// <returns></returns>
5 [DllImport("user32.dll")]
6 public static extern IntPtr GetDesktopWindow();
7 /// <summary>
8 /// 获取整个窗口的矩形区域
9 /// </summary>
10 /// <returns></returns>
11 [DllImport("user32.dll", SetLastError = true)]
12 public static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
13 /// <summary>
14 /// 检索整个窗口的设备上下文
15 /// </summary>
16 /// <param name="hWnd">具有要检索的设备上下文的窗口的句柄</param>
17 /// <returns></returns>
18 [DllImport("user32.dll", SetLastError = true)]
19 public static extern IntPtr GetWindowDC(IntPtr hWnd);
20 /// <summary>
21 /// 创建与指定设备兼容的内存设备上下文
22 /// </summary>
23 /// <param name="hdc">现有 DC 的句柄</param>
24 /// <returns>如果函数成功,则返回值是内存 DC 的句柄,否则返回Null</returns>
25 [DllImport("gdi32.dll")]
26 public static extern IntPtr CreateCompatibleDC([In] IntPtr hdc);
27 /// <summary>
28 /// 将对象选择到指定的设备上下文中
29 /// </summary>
30 /// <param name="hdc">DC 的句柄</param>
31 /// <param name="gdiObj">要选择的对象句柄</param>
32 /// <returns>如果函数成功,则返回值是兼容位图 (DDB) 的句柄,否则返回Null</returns>
33 [DllImport("gdi32.dll")]
34 public static extern IntPtr SelectObject([In] IntPtr hdc, [In] IntPtr gdiObj);
35 /// <summary>
36 /// 创建与与指定设备上下文关联的设备的位图
37 /// </summary>
38 /// <param name="hdc">设备上下文的句柄</param>
39 /// <param name="nWidth">位图宽度(以像素为单位)</param>
40 /// <param name="nHeight">位图高度(以像素为单位)</param>
41 /// <returns></returns>
42 [DllImport("gdi32.dll")]
43 public static extern IntPtr CreateCompatibleBitmap([In] IntPtr hdc, int nWidth, int nHeight);
44 /// <summary>
45 /// 执行与从指定源设备上下文到目标设备上下文中的像素矩形对应的颜色数据的位块传输
46 /// </summary>
47 /// <param name="hdcDest">目标设备上下文的句柄</param>
48 /// <param name="xDest">目标矩形左上角的 x 坐标(逻辑单位)</param>
49 /// <param name="yDest">目标矩形左上角的 y 坐标(逻辑单位)</param>
50 /// <param name="wDest">源矩形和目标矩形的宽度(逻辑单位)</param>
51 /// <param name="hDest">源矩形和目标矩形的高度(逻辑单位)</param>
52 /// <param name="hdcSource">源设备上下文的句柄</param>
53 /// <param name="xSrc">源矩形左上角的 x 坐标(逻辑单位)</param>
54 /// <param name="ySrc">源矩形左上角的 y 坐标(逻辑单位)</param>
55 /// <param name="rop">定义如何将源矩形的颜色数据与目标矩形的颜色数据相结合</param>
56 /// <returns></returns>
57 [DllImport("gdi32.dll")]
58 public static extern bool BitBlt(IntPtr hdcDest,
59 int xDest, int yDest, int wDest, int hDest, IntPtr hdcSource,
60 int xSrc, int ySrc, CopyPixelOperation rop);
61 /// <summary>
62 /// 删除逻辑笔、画笔、字体、位图、区域或调色板,释放与对象关联的所有系统资源。
63 /// 删除对象后,指定的句柄将不再有效。
64 /// </summary>
65 /// <param name="hObject"></param>
66 /// <returns></returns>
67 [DllImport("gdi32.dll")]
68 public static extern bool DeleteObject(IntPtr hObject);
69 /// <summary>
70 /// 删除指定的设备上下文
71 /// </summary>
72 /// <param name="hdc">设备上下文的句设备上下文的句</param>
73 /// <returns></returns>
74 [DllImport("gdi32.dll")]
75 public static extern bool DeleteDC([In] IntPtr hdc);
76 /// <summary>
77 /// 释放设备上下文 (DC),释放它以供其他应用程序使用
78 /// </summary>
79 /// <param name="hWnd"></param>
80 /// <param name="hdc"></param>
81 /// <returns></returns>
82 [DllImport("user32.dll", SetLastError = true)]
83 public static extern bool ReleaseDC(IntPtr hWnd, IntPtr hdc);
84
85 /// <summary>
86 /// 定义一个矩形区域。
87 /// </summary>
88 [StructLayout(LayoutKind.Sequential)]
89 public struct RECT
90 {
91 /// <summary>
92 /// 矩形左侧的X坐标。
93 /// </summary>
94 public int Left;
95
96 /// <summary>
97 /// 矩形顶部的Y坐标。
98 /// </summary>
99 public int Top;
100
101 /// <summary>
102 /// 矩形右侧的X坐标。
103 /// </summary>
104 public int Right;
105
106 /// <summary>
107 /// 矩形底部的Y坐标。
108 /// </summary>
109 public int Bottom;
110
111 /// <summary>
112 /// 获取矩形的宽度。
113 /// </summary>
114 public int Width => Right - Left;
115
116 /// <summary>
117 /// 获取矩形的高度。
118 /// </summary>
119 public int Height => Bottom - Top;
120
121 /// <summary>
122 /// 初始化一个新的矩形。
123 /// </summary>
124 /// <param name="left">矩形左侧的X坐标。</param>
125 /// <param name="top">矩形顶部的Y坐标。</param>
126 /// <param name="right">矩形右侧的X坐标。</param>
127 /// <param name="bottom">矩形底部的Y坐标。</param>
128 public RECT(int left, int top, int right, int bottom)
129 {
130 Left = left;
131 Top = top;
132 Right = right;
133 Bottom = bottom;
134 }
135 }

还有一种比较简单的方法Graphics.CopyFromScreen,看看调用DEMO:

 1         private void GraphicsCaptureButton_OnClick(object sender, RoutedEventArgs e)
2 {
3 var image = CaptureScreen1();
4 CaptureImage.Source = ConvertBitmapToBitmapSource(image);
5 }
6 /// <summary>
7 /// 截图屏幕
8 /// </summary>
9 /// <returns></returns>
10 public static Bitmap CaptureScreen1()
11 {
12 IntPtr desktopWindow = GetDesktopWindow();
13 //获取窗口位置大小
14 GetWindowRect(desktopWindow, out var lpRect);
15 return CaptureScreenByGraphics(0, 0, lpRect.Width, lpRect.Height);
16 }
17 /// <summary>
18 /// 截图屏幕
19 /// </summary>
20 /// <param name="x">x坐标</param>
21 /// <param name="y">y坐标</param>
22 /// <param name="width">截取的宽度</param>
23 /// <param name="height">截取的高度</param>
24 /// <returns></returns>
25 public static Bitmap CaptureScreenByGraphics(int x, int y, int width, int height)
26 {
27 var bitmap = new Bitmap(width, height);
28 using var graphics = Graphics.FromImage(bitmap);
29 graphics.CopyFromScreen(x, y, 0, 0, new System.Drawing.Size(width, height), CopyPixelOperation.SourceCopy);
30 return bitmap;
31 }

Graphics.CopyFromScreen调用简单了很多,与GDI有什么区别?

Graphics.CopyFromScreen内部也是通过GDI.BitBlt来完成屏幕捕获的,封装了下提供更高级别、易胜的API。

测试了下,第一种方法Gdi32性能比Graphics.CopyFromScreen性能略微好一点,冷启动时更明显点,试了2次耗时大概少个10多ms。

所以对于一般应用场景,使用 Graphics.CopyFromScreen 就足够了,但如果你需要更高的控制权和性能优化,建议使用 Gdi32.BitBlt

kybs00/CaptureImageDemo (github.com)

.NET 窗口/屏幕截图的更多相关文章

  1. [OpenCV-Python] OpenCV 中的 Gui特性 部分 II

    部分 IIOpenCV 中的 Gui 特性 OpenCV-Python 中文教程(搬运)目录 4 图片 目标 • 在这里你将学会怎样读入一幅图像,怎样显示一幅图像,以及如何保存一幅图像 • 你将要学习 ...

  2. confluence乱码问题

    1.上传附件需要统一字体,以测试通过:宋体字.雅黑.黑体 2.系统已做编码优化,支持windows字体.如下: 点击查看 3.之前文件有乱码,请重新上传 4.编辑一个 Office 附件文档的要求 当 ...

  3. [OpenCV-Python] 4 图像读取

    文章目录 OpenCV-Python: II OpenCV 中的 Gui 特性 4 图片 4.1 读入图像 4.2 显示图像 4.3 保存图像 4.4 总结一下 OpenCV-Python: II O ...

  4. Windows GDI 窗口与 Direct3D 屏幕截图

    前言 Windows 上,屏幕截图一般是调用 win32 api 完成的,如果 C# 想实现截图功能,就需要封装相关 api.在 Windows 上,主要图形接口有 GDI 和 DirectX.GDI ...

  5. Delphi实现屏幕截图、窗口截图、指定区域截图

    Use Jpeg procedure TForm1.snapscreen(a,b,c,d:Integer); var bmpscreen:Tbitmap; jpegscreen:Tjpegimage; ...

  6. 使用Python保存屏幕截图(不使用PIL)

    起因 在极客学院讲授<使用Python编写远程控制程序>的课程中,涉及到查看被控制电脑屏幕截图的功能. 如果使用PIL,这个需求只需要三行代码: from PIL import Image ...

  7. 如何使用office2010插入屏幕截图

    当我们习惯了用QQ的屏幕截图之后,当某一天我们在一台没有装QQ的办公电脑上,它装着office2010,我们可以实现用office来截图吗?其实Office2010深藏着犀利的截图工具. 插入图片到文 ...

  8. 屏幕截图、录像FastStone Capture

    作为一款极其优秀好用的屏幕截图软件,FastStone Capture 具有体积小巧.功能强大.操作简便等优点,其方便的浮动工具条和便捷的快捷键堪称完美结合,截图后的图片编辑与保存选项也特别丰富和方便 ...

  9. WindowManager和WindowManager.LayoutParams的使用以及实现悬浮窗口的方法

    写Android程序的时候一般用WindowManager就是去获得屏幕的宽和高,来布局一些小的东西.基本上没有怎么看他的其他的接口. 这两天想写一个简单的类似于Toast的东西,自定义布局,突然发现 ...

  10. WinSnap屏幕截图 V4.5.6 官方最新版

    软件名称: WinSnap屏幕截图软件语言: 多国语言授权方式: 免费试用运行环境: Win7 / Vista / Win2003 / WinXP 软件大小: 2.7MB图片预览: 软件简介:WinS ...

随机推荐

  1. iOS开发环境theos开发环境搭建与介绍

    iOS开发环境theos开发环境搭建与介绍 标签(空格分隔): 越狱开发-第一篇 1. 环境准备 一台Mac,本人的机器是MacBook Air (13-inch, Mid 2013),系统是10.1 ...

  2. 利用QEMU模拟大端序机器

    简介 当前我们安装虚拟机,一般小端机器比较多,有时候想模拟大端机器测试程序,这时就有模拟大端机器的需求. 参考:利用 QEMU USER 模式运行 mips 程序 - sinpo828 - 博客园 ( ...

  3. kettle从入门到精通 第二十六课 再谈 kettle Transformation executor

    1.前面文章有学习过Transformation executor ,但后来测试kettle性能的时候遇到了很大的问题,此步骤的处理性能太慢,导致内存溢出等问题.所以再次一起学习下此步骤的用法. 2. ...

  4. 聊聊GLM-4-9B开源模型的微调loss计算

    概述 Github官方地址:GLM-4 网上已经有很多关于微调的文章,介绍各种方式下的使用,这里不会赘述.我个人比较关心的是微调时的loss计算逻辑,这点在很多的文章都不会有相关的描述,因为大多数人都 ...

  5. vite+vue3+ts+elementPlus前端框架搭建 [二] pinia状态管理

    前面已经完成了基本框架搭建,下一步针对各个模块的封装以及实验 本章主要是针对pinia的状态模块实现 1. 创建Store 在src文件夹下创建一个store的文件夹,并在该文件夹下创建index.t ...

  6. @Valid + BindingResult 拦截接口错误信息

    @Valid + BindingResult 拦截接口错误信息###测试发现: HttpServletRequest request, HttpServletResponse response, 需要 ...

  7. Lecture3

    Smiling & Weeping ---- 蝴蝶在双翼里藏匿夏的脉络 妄图在绿意中品鉴隆冬 第三章 Git分支管理 3.1 分支的简介 Git最重要的运用场景是多人协同开发,但是如何能保证每 ...

  8. 机器学习(一)——递归特征消除法实现SVM(matlab)

    机器学习方法对多维特征数据进行分类:本文用到非常经典的机器学习方法,使用递归特征消除进行特征选择,使用支持向量机构建分类模型,使用留一交叉验证的方法来评判模型的性能. 构建模型:支持向量机(Suppo ...

  9. 在Ubuntu 18.04 安装 adb

    Ubuntu下安装ADB 背景 电脑上的USB口有问题,不方便调试:发现用于开发的服务器就在工位旁边. 先拿过来用一下. Ubuntu:18.04 做法 安装adb 做法有很多种,列举下列2种. 下载 ...

  10. 高通参考设计中MTP与QRD

    高通参考设计中MTP与QRD 背景 之前在调试设备树的时候,看到设备树带了一个qrd的后缀,一直没搞清楚.上网找资料也好像不是我想要的. 今天查阅lk侧的代码,发现了HW_PLATFORM_HRD这个 ...