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