C# 活动窗体截图:基于 Win32 API 的实现

news2025/5/19 11:45:06
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) 
            {
                // 如果发生异常,确保释放Bitmap
                bmp?.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)
            {
                // 如果发生异常,确保释放Bitmap
                bmp?.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();
            }
        }

    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2379254.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

服务器防文件上传手写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…

计算机的基本组成与性能

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、隐式实例化&#xff1a;让编译器根据实参推演模板参数的实际类型2.4.2、显示实例化&#xff1a;在函数名后的<>中指定模板参数的实际类型 …

【大模型系列】logprobs(对数概率)参数

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

C语言内存函数与数据在内存中的存储

一、c语言内存函数 1、memcpy函数是一个标准库函数&#xff0c;用于内存复制。功能上是用来将一块内存中的内容复制到另一块内存中。用户需要提供目标地址、源地址以及要复制的字节数。例如结构体之间的复制。 memcpy函数的原型是&#xff1a;void* memcpy&#xff08;void* …

通过MCP让LLM调用系统接口

场景 MCP的出现大大丰富了LLM的功能&#xff0c;对于存量系统&#xff0c;我们希望能让模型调用已有的接口&#xff0c;以最小的成本让AI能够获取系统内部数据。因此我们开发了一个名为http-api-call的MCP Server&#xff0c;来支持模型到内部API的调用 实现方案 使用用标准…

【刚下赛场!】2025年江西省电子专题赛 - 现场制作:简易数控直流电流源原题

一、题目要求 二、赛场注意事项 1、一定要用铜柱将板子升起来&#xff0c;不然我们剪下来的引脚在测试的时候放在桌子上非常容易导致我们的板子短路&#xff08;记得把铜柱卸下来再上交作品&#xff0c;不然会被认为是做标记判0分&#xff09;&#xff1b; 2、发下来器件之后…

材料×工艺×AI:猎板PCB重构汽车电子四层板技术逻辑

一、汽车电子四层板的三大核心挑战 1. 极端环境下的可靠性保障 汽车电子需在-40℃至150℃的剧烈温变、高湿振动等环境中稳定运行。例如&#xff0c;电池管理系统&#xff08;BMS&#xff09;要求PCB在高温下阻抗漂移率低于8%&#xff0c;且镀层需具备抗腐蚀能力。猎板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 测…

Spring AOP从0到1

Spring有两大核心&#xff1a; 1、IoC 控制反转 2、AOP 面向切面编程 AOP&#xff1a;切面就是指某⼀类特定问题, 所以AOP也可以理解为面向特定⽅法编程. 引入AOP依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spri…

软考IPSEC案例分析

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

C++(23):容器类<vector>

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

Hugo 安装保姆级教程(搭建个人blog)

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

tomcat查看状态页及调优信息

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

从坏道扫描到错误修复:HD Tune实战指南

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

将嵌入映射到 Elasticsearch 字段类型:semantic_text、dense_vector、sparse_vector

作者&#xff1a; Andre Luiz 讨论如何以及何时使用 semantic_text、dense_vector 或 sparse_vector&#xff0c;以及它们与嵌入生成的关系。 通过这个自定进度的 Search AI 实践学习亲自体验向量搜索。你可以开始免费云试用&#xff0c;或者在本地机器上尝试 Elastic。 多年来…

解决uni-app开发中的“TypeError: Cannot read property ‘0‘ of undefined“问题

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