当前位置: 首页 > news >正文

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();}}}
}

http://www.xdnf.cn/news/462421.html

相关文章:

  • 有效的括号
  • 【蓝桥杯省赛真题49】python偶数 第十五届蓝桥杯青少组Python编程省赛真题解析
  • ROS--NAVI DWA
  • 【c语言】动态内存分配
  • MySQL 迁移至 Doris 最佳实践方案
  • 低功耗实现方法思路总结
  • 策略模式-枚举实现
  • 如何判断一个网站后端是用什么语言写的
  • 7.Pyecharts:全局配置项1
  • Python 翻译词典小程序
  • 平替BioLegend品牌-Elabscience FITC Anti-Mouse CD8a抗体(53-6.7)精准标记T细胞表面抗原
  • 断点续传使用场景,完整前后端实现示例,包括上传,下载,验证
  • 麒麟系统ARM64架构部署mysql、jdk和java项目
  • 牛客网刷题:NC208813求逆序数
  • 【PX4飞控】在 Matlab Simulink 中使用 Mavlink 协议与 PX4 飞行器进行交互
  • python处理异常,JSON
  • 数据结构—排序(斐波那契数列,冒泡,选择,插入,快速,归并,图,广度优先算法)
  • NSSCTF [GFCTF 2021]where_is_shell
  • 【MySQL】多表连接查询
  • postgresql主从+repmgr+keepalive安装
  • Google DeepMind 推出AlphaEvolve
  • Trivy:让你时刻掌控的开源安全扫描器
  • 产线视觉检测设备技术方案:基于EFISH-SCB-RK3588/SAIL-RK3588的国产化替代赛扬N100/N150全场景技术解析
  • SQL:MySQL函数:条件函数(Conditional Functions)
  • OpenCV人脸识别EigenFace算法、案例解析
  • [学习]RTKLib详解:tle.c(系列终章)
  • 一般的析因设计
  • 探索Turn.js:打造惊艳的3D翻页效果
  • 2025年,如何制作并部署一个完整的个人博客网站
  • 让三个线程(t1、t2、t3)按顺序依次打印 A、B、C