相比于 Windows 2000 引入到 GDI+ 中的 BitBlt
方法截取窗口图片,Windows XP 时也引入了 PrintWindow
方法来专门截取窗口,截取的原理也不同。
微软 Office 系列里的截取窗口,用的就是 PrintWindow
方法。
开始截图
相比于使用 BitBlt
方法,使用 PrintWindow
截取窗口的代码少得多。
你需要引用如下命名空间:
1
2
3
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Graphics;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static byte[] CaptureWindow(HWND hWnd, int width, int height)
{
using (var bmp = new Bitmap(width, height))
{
using (Graphics memoryGraphics = Graphics.FromImage(bmp))
{
IntPtr dc = memoryGraphics.GetHdc();
PrintWindow(hWnd, dc, 0);
memoryGraphics.ReleaseHdc(dc);
using (MemoryStream ms = new MemoryStream())
{
bmp.Save(ms, ImageFormat.Png);
ms.Seek(0, SeekOrigin.Begin);
return ms.ToArray();
}
}
}
}
[DllImport("User32.dll", SetLastError = true)]
static extern bool PrintWindow(IntPtr hwnd, IntPtr hdc, uint nFlags);
原理、效果和问题
使用 PrintWindow
来截图时,目标窗口会收到一次 WM_PRINT
或 WM_PRINTCLIENT
消息以完成一次绘图。并且,此过程是同步进行的,如果目标窗口在处理消息时没有返回,那么这里的调用将一直挂起。
使用此方法截图时,DWM 绘制的窗口部分在真实窗口中和实际截出来的会不一样,是关掉了 Aero 效果时的窗口样式。
当然,还有可能把目标窗口截挂:
更多截窗口方法
- Win32/C# 应用使用 GDI+ 对窗口截图(BitBlt) - walterlv
- Win32/C# 应用不依赖任何库使用纯 GDI+ 对窗口截图(BitBlt) - walterlv
- (本文)Win32/C# 应用使用 PrintWindow 对窗口截图(PrintWindow) - walterlv
参考资料
本文会经常更新,请阅读原文: https://blog.walterlv.com/post/win32-capture-window-using-print-window.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接: https://blog.walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 ([email protected]) 。