首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

.NET 窗口/屏幕截图

编程知识
2024年07月30日 13:38

图像采集源除了显示控件(上一篇《.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下拿到的桌面信息-句柄获取桌面窗口的设备上下文,再以设备上下文分别创建内存设备上下文、设备位图句柄

 1 BOOL BitBlt(
 2     HDC   hdcDest,  // 目标设备上下文
 3     int   nXDest,   // 目标起始x坐标
 4     int   nYDest,   // 目标起始y坐标
 5     int   nWidth,   // 宽度(像素)
 6     int   nHeight,  // 高度(像素)
 7     HDC   hdcSrc,   // 源设备上下文
 8     int   nXSrc,    // 源起始x坐标
 9     int   nYSrc,    // 源起始y坐标
10     DWORD dwRop    // 操作码(如CopyPixelOperation.SourceCopy)
11 );

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

另外,与Bitblt差不多的还有StretchBlt,StretchBlt也是复制图像,但可以同时对图像进行拉伸或者缩小,需要缩略图可以用这个方法

然后以设备位图句柄输出一个位图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     }
View Code

还有一种比较简单的方法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)

From:https://www.cnblogs.com/kybs0/p/18330803
本文地址: http://shuzixingkong.net/article/594
0评论
提交 加载更多评论
其他文章 EdgeOne安全专项实践:上传文件漏洞攻击详解与防范措施
通过本文,我们深入探讨了文件上传漏洞攻击的多种案例和防范措施,以及在搭建攻击靶场时的实际操作。从前端和后端的校验漏洞,到利用Apache配置文件和文件包含漏洞的攻击方式,每一步都展示了安全防护的重要性。
EdgeOne安全专项实践:上传文件漏洞攻击详解与防范措施 EdgeOne安全专项实践:上传文件漏洞攻击详解与防范措施 EdgeOne安全专项实践:上传文件漏洞攻击详解与防范措施
RAG+AI工作流+Agent:LLM框架该如何选择,全面对比MaxKB、Dify、FastGPT、RagFlow、Anything-LLM,以及更多推荐
RAG+AI工作流+Agent:LLM框架该如何选择,全面对比MaxKB、Dify、FastGPT、RagFlow、Anything-LLM,以及更多推荐
RAG+AI工作流+Agent:LLM框架该如何选择,全面对比MaxKB、Dify、FastGPT、RagFlow、Anything-LLM,以及更多推荐 RAG+AI工作流+Agent:LLM框架该如何选择,全面对比MaxKB、Dify、FastGPT、RagFlow、Anything-LLM,以及更多推荐 RAG+AI工作流+Agent:LLM框架该如何选择,全面对比MaxKB、Dify、FastGPT、RagFlow、Anything-LLM,以及更多推荐
[HTTP] HTTP 协议 Response Header 之 Content-Length、Transfer-Encoding与Content-Encoding
0 引言 在近期项目一场景中,一 Web API (响应内容:7MB - 40MB、数据项:5W-20W条)的网络传输耗时较大,短则 5s,长则高达25s,前端渲染又需要耗时 9s-60s。 在这个场景中,前端的问题暂且不表。那么针对后端的问题,个人认为还是有较大的优化空间: 1)启用HTTP 内容
[HTTP] HTTP 协议 Response Header 之 Content-Length、Transfer-Encoding与Content-Encoding [HTTP] HTTP 协议 Response Header 之 Content-Length、Transfer-Encoding与Content-Encoding [HTTP] HTTP 协议 Response Header 之 Content-Length、Transfer-Encoding与Content-Encoding
《最新出炉》系列初窥篇-Python+Playwright自动化测试-58 - 文件下载
1.简介 前边几篇文章讲解完如何上传文件,既然有上传,那么就可能会有下载文件。因此宏哥就接着讲解和分享一下:自动化测试下载文件。可能有的小伙伴或者童鞋们会觉得这不是很简单吗,还用你介绍和讲解啊,不说就是访问到下载页面,然后定位到要下载的文件的下载按钮后,点击按钮就可以了。其实不是这样的,且听宏哥徐徐
《最新出炉》系列初窥篇-Python+Playwright自动化测试-58 - 文件下载 《最新出炉》系列初窥篇-Python+Playwright自动化测试-58 - 文件下载 《最新出炉》系列初窥篇-Python+Playwright自动化测试-58 - 文件下载
从pytest源码的角度分析pytest工作原理
从pytest源码的角度分析pytest工作原理 从 pytest 源代码的角度来分析其工作原理,我们需要关注几个关键的部分,特别是 pytest 的启动过程以及测试的收集与执行。下面是基于 pytest 源代码的一个高层次的概述。 pytest 的启动过程 命令行解析: pytest&#160;的
从pytest源码的角度分析pytest工作原理
Docker镜像构建:技术深度解析与实践指南
本文深入分析了Docker镜像构建的技术细节,从基础概念到高级技术,涵盖了多阶段构建、安全性优化、性能提升及实战案例。旨在为专业人士提供全面的技术洞察和实用指导,以提升Docker镜像构建的效率和安全性。 关注【TechLeadCloud】,分享互联网架构、云服务技术的全维度知识。作者拥有10+年互
Docker镜像构建:技术深度解析与实践指南 Docker镜像构建:技术深度解析与实践指南
[rCore学习笔记 019]在main中测试本章实现
写在前面 本随笔是非常菜的菜鸡写的。如有问题请及时提出。 可以联系:1160712160@qq.com GitHhub:https://github.com/WindDevil (目前啥也没有 批处理操作系统的启动和运行流程 要想把本章实现的那些模块全部都串联在一起以实现运行一个批处理操作系统,回顾
[rCore学习笔记 019]在main中测试本章实现
Known框架实战演练——进销存财务管理
本文介绍如何实现进销存管理系统的财务对账模块,财务对账模块包括供应商对账和客户对账2个菜单页面。供应商和客户对账字段相同,因此可共用一个页面组件类。 项目代码:JxcLite 开源地址: https://gitee.com/known/JxcLite 1. 配置模块 运行项目,在【系统管理-模块管理