C# 活动窗体截图:基于 Win32 API 的实现
1. 核心功能与技术栈
该截图功能类 ScreenShotClass
基于 Win32 API 实现了两种截图方式:
CopyFromScreen
方法:利用Graphics.CopyFromScreen
直接截取屏幕区域。BitBlt
方法:通过GDI+
的位图块传输(BitBlt)实现窗口截图。
核心依赖的 Win32 API 包括:
user32.dll
:获取窗口句柄、窗口矩形区域。dwmapi.dll
:获取窗口扩展边界(适用于现代 Windows 窗口阴影等效果)。gdi32.dll
:执行位图复制操作(BitBlt)。
DwmGetWindowAttribute与 GetWindowRect区别
?
GetWindowRect
返回窗口边框矩形(不含阴影等视觉扩展),而 DwmGetWindowAttribute
能获取实际显示区域,确保截图完整。
2. 两种截图方式对比
方法 | 实现原理 |
---|---|
CopyFromScreen | 使用 Graphics.CopyFromScreen 直接从屏幕坐标复制像素到目标位图。 |
BitBlt | 通过 GetWindowDC 获取窗口 DC,再用 BitBlt 复制像素到目标 DC(位图)。 |
适用场景与限制
- 适用场景:
- 截取当前活动窗口或指定窗口内容。
- 需要包含窗口边框、阴影等视觉元素的精确截图。
- 限制:
- 性能影响:频繁调用
BitBlt
可能影响 UI 线程,建议异步执行。 - 兼容性:仅适用于 Windows 系统,依赖 Win32 API。
- 性能影响:频繁调用
3. 使用示例
// 截取前台窗口(使用 CopyFromScreen)
try {Image screenshot = ScreenShotClass.Screenshot_CopyFromScreen();screenshot.Save("screenshot.png", System.Drawing.Imaging.ImageFormat.Png);
} catch (Exception ex) {Console.WriteLine($"截图失败:{ex.Message}");
}// 截取指定窗口(使用 BitBlt)
IntPtr targetHandle = ...; // 获取目标窗口句柄(如通过 FindWindow)
Image screenshot = ScreenShotClass.Screenshot_CopyFromDC(targetHandle);
完整代码:
using System;
using System.Drawing;
using System.Runtime.InteropServices;namespace CSharp学习之截图功能
{public static class Win32Api{// 获取前台窗口句柄[DllImport("user32.dll")]public static extern IntPtr GetForegroundWindow();// 获取窗口属性(这里用于获取扩展框架边界)[DllImport("dwmapi.dll")]public static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, out RECT pvAttribute, int cbAttribute);// 获取窗口的矩形区域(包括边框、标题栏等)[DllImport("user32.dll")]public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);// 获取指定窗口的设备上下文(DC)[DllImport("user32.dll")]public static extern IntPtr GetWindowDC(IntPtr hWnd);// 释放设备上下文(DC)[DllImport("user32.dll")]public static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);// 执行位块传输(BitBlt)操作,用于复制图像[DllImport("gdi32.dll")]public static extern bool BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, int dwRop);// 定义RECT结构体,用于表示矩形区域[StructLayout(LayoutKind.Sequential)]public struct RECT{public int Left;public int Top;public int Right;public int Bottom;// 计算矩形的宽度public int Width => Right - Left;// 计算矩形的高度public int Height => Bottom - Top;}// DWMWA_EXTENDED_FRAME_BOUNDS常量public const int DWMWA_EXTENDED_FRAME_BOUNDS = 9;// SRCCOPY表示源图像直接复制到目标设备上下文public const int SRCCOPY = 0x00CC0020;}public static class ScreenShotClass{public static Image Screenshot_CopyFromScreen(){IntPtr handle = Win32Api.GetForegroundWindow();Win32Api.RECT rect;int result = Win32Api.DwmGetWindowAttribute(handle, Win32Api.DWMWA_EXTENDED_FRAME_BOUNDS, out rect, Marshal.SizeOf(typeof(Win32Api.RECT)));if (result != 0){throw new InvalidOperationException("无法获取窗口边界");}Bitmap bmp = new Bitmap(rect.Width, rect.Height);try{using (Graphics g = Graphics.FromImage(bmp)){g.CopyFromScreen(rect.Left, rect.Top, 0, 0, bmp.Size);}return bmp;}catch(Exception ex) {// 如果发生异常,确保释放Bitmapbmp?.Dispose();throw new InvalidOperationException($"截图失败: {ex.Message}");}}public static Image Screenshot_CopyFromScreen(IntPtr handle){Win32Api.RECT rect;int result = Win32Api.DwmGetWindowAttribute(handle, Win32Api.DWMWA_EXTENDED_FRAME_BOUNDS, out rect, Marshal.SizeOf(typeof(Win32Api.RECT)));if (result != 0){throw new InvalidOperationException("无法获取窗口边界");}Bitmap bmp = new Bitmap(rect.Width, rect.Height);try{using (Graphics g = Graphics.FromImage(bmp)){g.CopyFromScreen(rect.Left, rect.Top, 0, 0, bmp.Size);}return bmp;}catch (Exception ex){// 如果发生异常,确保释放Bitmapbmp?.Dispose();throw new InvalidOperationException($"截图失败: {ex.Message}");}}public static Image Screenshot_CopyFromDC(){IntPtr handle = Win32Api.GetForegroundWindow();Win32Api.RECT rect;Win32Api.GetWindowRect(handle, out rect);Bitmap bmp = new Bitmap(rect.Width, rect.Height);Graphics g = null;IntPtr hdcSrc = IntPtr.Zero;IntPtr hdcDest = IntPtr.Zero;try{g = Graphics.FromImage(bmp);hdcDest = g.GetHdc();hdcSrc = Win32Api.GetWindowDC(handle);if (hdcSrc == IntPtr.Zero){throw new InvalidOperationException("无法获取窗口设备上下文");}bool success = Win32Api.BitBlt(hdcDest, 0, 0, rect.Width, rect.Height, hdcSrc, 0, 0, Win32Api.SRCCOPY);if (!success){throw new InvalidOperationException("BitBlt 调用失败");}return bmp;}catch (Exception ex){// 释放资源bmp?.Dispose();throw new InvalidOperationException($"截图失败: {ex.Message}");}finally{// 确保释放资源if (hdcDest != IntPtr.Zero && g != null){g.ReleaseHdc(hdcDest);}if (hdcSrc != IntPtr.Zero){Win32Api.ReleaseDC(handle, hdcSrc);}g?.Dispose();}}public static Image Screenshot_CopyFromDC(IntPtr handle){Win32Api.RECT rect;Win32Api.GetWindowRect(handle, out rect);Bitmap bmp = new Bitmap(rect.Width, rect.Height);Graphics g = null;IntPtr hdcSrc = IntPtr.Zero;IntPtr hdcDest = IntPtr.Zero;try{g = Graphics.FromImage(bmp);hdcDest = g.GetHdc();hdcSrc = Win32Api.GetWindowDC(handle);if (hdcSrc == IntPtr.Zero){throw new InvalidOperationException("无法获取窗口设备上下文");}bool success = Win32Api.BitBlt(hdcDest, 0, 0, rect.Width, rect.Height, hdcSrc, 0, 0, Win32Api.SRCCOPY);if (!success){throw new InvalidOperationException("BitBlt 调用失败");}return bmp;}catch (Exception ex){// 释放资源bmp?.Dispose();throw new InvalidOperationException($"截图失败: {ex.Message}");}finally{// 确保释放资源if (hdcDest != IntPtr.Zero && g != null){g.ReleaseHdc(hdcDest);}if (hdcSrc != IntPtr.Zero){Win32Api.ReleaseDC(handle, hdcSrc);}g?.Dispose();}}}
}
相关文章:
C# 活动窗体截图:基于 Win32 API 的实现
1. 核心功能与技术栈 该截图功能类 ScreenShotClass 基于 Win32 API 实现了两种截图方式: CopyFromScreen 方法:利用 Graphics.CopyFromScreen 直接截取屏幕区域。BitBlt 方法:通过 GDI 的位图块传输(BitBlt)实现窗口…...

服务器防文件上传手写waf
一、waf的目录结构,根据自己目录情况进行修改 二、创建文件夹以及文件 sudo mkdir -p /www/server/waf-monitor sudo mkdir -p /www/server/waf-monitor/quarantine #创建文件夹 chmod 755 /www/server/waf-monitor #赋权cd /www/server/waf-monitor/touch waf-m…...
大模型为什么学新忘旧(大模型为什么会有灾难性遗忘)?
字数:2500字 一、前言:当学霸变成“金鱼” 假设你班上有个学霸,数学考满分,英语拿第一,物理称霸全校。某天,他突然宣布:“我要全面发展!从今天起学打篮球!” 一周后&am…...

计算机的基本组成与性能
1. 冯诺依曼体系结构:计算机组成的金字塔 1.1. 计算机的基本硬件组成 1.CPU - 中央处理器(Central Processing Unit)。 2.内存(Memory)。 3.主板(Motherboard)。主板的芯片组(Ch…...

linux下编写shell脚本一键编译源码
0 前言 进行linux应用层编程时,经常会使用重复的命令对源码进行编译,然后把编译生成的可执行文件拷贝到工作目录,操作非常繁琐且容易出错。本文编写一个简单的shell脚本一键编译源码。 1 linux下编写shell脚本一键编译源码 shell脚本如下&…...

【深度学习】#12 计算机视觉
主要参考学习资料: 《动手学深度学习》阿斯顿张 等 著 【动手学深度学习 PyTorch版】哔哩哔哩跟李沐学AI 目录 目标检测锚框交并比(IoU)锚框标注真实边界框分配偏移量计算损失函数 非极大值抑制预测 多尺度目标检测单发多框检测(S…...

Baklib赋能企业知识资产AI化升级
AI驱动知识管理革新 在数字化转型浪潮中,企业知识管理的范式正经历AI技术的深度重构。传统知识库受限于静态存储与人工维护,而Baklib通过构建知识中台架构,将多模态数据处理与语义理解引擎深度融合,实现知识资产的动态聚合与智能…...

【C++】模板上(泛型编程) —— 函数模板与类模板
文章目录 一、啥是泛型编程二、函数模板2.1、函数模板的概念2.2、函数模板的格式2.3、函数模板的原理2.4、函数模板的实例化2.4.1、隐式实例化:让编译器根据实参推演模板参数的实际类型2.4.2、显示实例化:在函数名后的<>中指定模板参数的实际类型 …...
软件架构之--论微服务的开发方法1
论微服务的开发方法1 摘要 2023年 2月,本人所在集团公司承接了长三角地区某省渔船图纸电子化审查系统项目开发,该项目旨在为长三角地区渔船建造设计院、以及渔船图纸审查机构提供一个便捷的渔船图纸电子化审查服务平台。在此项目中,我作为项目组成员参与项目的建设工作,并…...

【大模型系列】logprobs(对数概率)参数
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...

C语言内存函数与数据在内存中的存储
一、c语言内存函数 1、memcpy函数是一个标准库函数,用于内存复制。功能上是用来将一块内存中的内容复制到另一块内存中。用户需要提供目标地址、源地址以及要复制的字节数。例如结构体之间的复制。 memcpy函数的原型是:void* memcpy(void* …...
代码案例分析
以下是一个使用线性回归进行简单房价预测的机器学习代码案例分析: 代码示例 import numpy as np import matplotlib.pyplot as plt from sklearn.linear_model import LinearRegression from sklearn.model_selection import train_test_split # 生成一些示例数据…...

通过MCP让LLM调用系统接口
场景 MCP的出现大大丰富了LLM的功能,对于存量系统,我们希望能让模型调用已有的接口,以最小的成本让AI能够获取系统内部数据。因此我们开发了一个名为http-api-call的MCP Server,来支持模型到内部API的调用 实现方案 使用用标准…...
如何利用Redis实现延迟队列?
延迟队列概念解析 延迟队列(Delay Queue)是一种特殊的消息队列,核心特性是允许消息在指定的延迟时间后被消费者处理,而非立即消费。它解决了传统队列(FIFO)无法处理“定时任务”或“超时任务”的问题&…...

【刚下赛场!】2025年江西省电子专题赛 - 现场制作:简易数控直流电流源原题
一、题目要求 二、赛场注意事项 1、一定要用铜柱将板子升起来,不然我们剪下来的引脚在测试的时候放在桌子上非常容易导致我们的板子短路(记得把铜柱卸下来再上交作品,不然会被认为是做标记判0分); 2、发下来器件之后…...

材料×工艺×AI:猎板PCB重构汽车电子四层板技术逻辑
一、汽车电子四层板的三大核心挑战 1. 极端环境下的可靠性保障 汽车电子需在-40℃至150℃的剧烈温变、高湿振动等环境中稳定运行。例如,电池管理系统(BMS)要求PCB在高温下阻抗漂移率低于8%,且镀层需具备抗腐蚀能力。猎板PCB通…...

MCP(一)——QuickStart
目录 1. MCP简介2. MCP的优势3. MCP核心4. QuickStart For Server Developers(仅具参考)4.1 MCP核心概念4.2 构建MCP服务器的代码4.2.1 设置MCP服务器实例4.2.2 辅助函数4.2.3 实现工具执行4.2.4 在Cherry-Studio中添加MCP服务器4.2.5 演示4.2.5.1 测试工具get_alerts4.2.5.2 测…...
GCC 版本与C++ 标准对应关系
GCC 版本 与支持的 C 标准(C11、C14、C17、C20、C23) 的对应关系 GCC 版本与 C 标准支持对照表 GCC 版本默认 C 标准C11C14C17C20C23GCC 4.8C98✅ (部分支持)❌❌❌❌GCC 4.9C98✅ (完整支持)❌❌❌❌GCC 5.1C98✅✅ (完整支持)❌❌❌GCC 6.1C14✅✅✅ …...

Spring AOP从0到1
Spring有两大核心: 1、IoC 控制反转 2、AOP 面向切面编程 AOP:切面就是指某⼀类特定问题, 所以AOP也可以理解为面向特定⽅法编程. 引入AOP依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spri…...
JavaScript 中的 Document 对象详解
JavaScript 中的 Document 对象详解 一、Document 对象概述 1. 定义与作用 Document 对象是浏览器中 HTML 文档的入口点,是 Window 对象的属性(即 window.document)。它代表整个 HTML 页面,提供了操作和访问页面内容的方法和属性,是 DOM(文档对象模型)的核心。 2. 核…...
archlinux按键映射按键自定义
我想把右ALT映射成Super键,也就是mod4键位,折腾了半天没有成功。问AI也没有解决,与是只好自己去看wiki了,发现原来很简单。只是我没有clear。 https://wiki.archlinuxcn.org/wiki/Xmodmap 安装xorg sudo pacman -S xorg直接选择…...
【python】字典和数组的数组
一、字典是由键值对(key-value)组成的 因为 results[num] {...} 这种写法是通过键(这里是 num)为 results 赋值,results 就是一个字典(dict)。 在 Python 里,字典是由键值对&#…...

软考IPSEC案例分析
要回忆IPSEC点击这里 题目 5/21 某全国连锁企业的总部和分布在全国各地的30家分公司之间经常需要传输各种内部数据,因此公司决定在总部和各分公司之间建立VPN技术。具体拓扑如下: 配置部分只显示了与总部与分公司1的配置。 根据拓扑完成问题1-问题2。…...

C++(23):容器类<vector>
目录 一、核心概念 二、基本语法 1. 头文件 2. 声明与初始化 三、常用操作 四、具体实例 1、size()、front()、back() 2、push_back()、pop_back()、capacity() 3、reserve() 一、核心概念 Vectors 包含着一系列连续存储的元素,其行为…...

Hugo 安装保姆级教程(搭建个人blog)
Hogo 安装保姆级教程 友链 参考文章: https://blog.csdn.net/xianyun_0355/article/details/140261279 前言 Hugo 是 Go 编写的静态网站生成器,速度快,易用,可配置。作为一款跨平台开源建站系统,当前提供 Windows&…...

tomcat查看状态页及调优信息
准备工作 先准备一台已经安装好tomcat的虚拟机,tomcat默认是状态页是默认被禁用的 1.添加授权用户 vim /usr/local/tomcat/conf/tomcat-users.xml22 <role rolename"manager-gui"/>23 <user username"admin" password"tomcat&q…...

从坏道扫描到错误修复:HD Tune实战指南
一、硬盘检测的必要性 随着计算机使用时间的增加,机械硬盘和固态硬盘都会出现不同程度的性能衰减。定期进行硬盘健康检查可以:及时发现潜在故障;预防数据丢失风险;掌握存储设备实际状态。 二、HD Tune功能解析 性能测试&#x…...

将嵌入映射到 Elasticsearch 字段类型:semantic_text、dense_vector、sparse_vector
作者: Andre Luiz 讨论如何以及何时使用 semantic_text、dense_vector 或 sparse_vector,以及它们与嵌入生成的关系。 通过这个自定进度的 Search AI 实践学习亲自体验向量搜索。你可以开始免费云试用,或者在本地机器上尝试 Elastic。 多年来…...
【LeetCode 热题100】17:电话号码的字母组合(详细解析)(Go语言版)
☎️ LeetCode 17. 电话号码的字母组合(回溯 DFS 详解) 📌 题目描述 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按任意顺序返回。 数字到字母的映射如下(与电话按键相同)…...

解决uni-app开发中的“TypeError: Cannot read property ‘0‘ of undefined“问题
问题背景 在使用uni-app开发小程序或App时,你可能会遇到这样一个错误: TypeError: Cannot read property 0 of undefinedat uni.promisify.adaptor.js:7这个错误看起来很唬人,但它实际上与uni-app框架中的Promise适配器有关。今天,我们将深…...