C#使用BitBlt进行窗口抓图
本文和C++使用BitBlt进行窗口抓图对应,使用C#实现。
这种方式对1920*1080大小的窗口,一次抓图的时间参考(VS2015+i5 9400F):低至2~3ms(平均4.3ms)。
参见:C#抓图服务。
1、Win32封装
Win32Consts
using System.ComponentModel; namespace CaptureSharp { public sealed class Win32Consts { public enum DibColorMode : uint { DIB_RGB_COLORS = 0x00, DIB_PAL_COLORS = 0x01, DIB_PAL_INDICES = 0x02 } public enum BitmapCompressionMode : uint { BI_RGB = 0, BI_RLE8 = 1, BI_RLE4 = 2, BI_BITFIELDS = 3, BI_JPEG = 4, BI_PNG = 5 } public enum RasterOperationMode : uint { SRCCOPY = 0x00CC0020, SRCPAINT = 0x00EE0086, SRCAND = 0x008800C6, SRCINVERT = 0x00660046, SRCERASE = 0x00440328, NOTSRCCOPY = 0x00330008, NOTSRCERASE = 0x001100A6, MERGECOPY = 0x00C000CA, MERGEPAINT = 0x00BB0226, PATCOPY = 0x00F00021, PATPAINT = 0x00FB0A09, PATINVERT = 0x005A0049, DSTINVERT = 0x00550009, BLACKNESS = 0x00000042, WHITENESS = 0x00FF0062, CAPTUREBLT = 0x40000000 //only if WinVer >= 5.0.0 (see wingdi.h) } public enum PrintWindowMode : uint { [Description( "Only the client area of the window is copied to hdcBlt. By default, the entire window is copied.")] PW_CLIENTONLY = 0x00000001, [Description("works on windows that use DirectX or DirectComposition")] PW_RENDERFULLCONTENT = 0x00000002 } } }
Win32Types
using System.Runtime.InteropServices; namespace CaptureSharp { public sealed class Win32Types { [StructLayout(LayoutKind.Sequential)] public struct Point { public int x; public int y; public Point(int x, int y) { this.x = x; this.y = y; } } [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; } [StructLayout(LayoutKind.Sequential, Pack = 2)] public struct BitmapFileHeader { public ushort bfType; public uint bfSize; public ushort bfReserved1; public ushort bfReserved2; public uint bfOffBits; } [StructLayout(LayoutKind.Sequential)] public struct BitmapInfoHeader { public uint biSize; public int biWidth; public int biHeight; public ushort biPlanes; public ushort biBitCount; public uint biCompression; public uint biSizeImage; public int biXPelsPerMeter; public int biYPelsPerMeter; public uint biClrUsed; public uint biClrImportant; public void Init() { biSize = (uint)Marshal.SizeOf(this); } } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct RgbQuad { public byte rgbBlue; public byte rgbGreen; public byte rgbRed; public byte rgbReserved; } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct BitmapInfo { public BitmapInfoHeader bmiHeader; public RgbQuad bmiColors; } } }
Win32Funcs
using System; using System.Runtime.InteropServices; namespace CaptureSharp { public sealed class Win32Funcs { [DllImport("User32.dll", SetLastError = true)] public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool GetWindowRect(IntPtr hWnd, out Win32Types.Rect lpRect); [DllImport("user32.dll")] public static extern bool GetClientRect(IntPtr hWnd, out Win32Types.Rect lpRect); [DllImport("user32.dll", EntryPoint = "GetWindowDC")] public static extern IntPtr GetWindowDC(IntPtr hWnd); [DllImport("gdi32.dll")] public static extern IntPtr CreateCompatibleDC(IntPtr hDc); [DllImport("gdi32.dll")] public static extern IntPtr CreateCompatibleBitmap(IntPtr hDc, int nWidth, int nHeight); [DllImport("gdi32.dll")] public static extern bool DeleteDC(IntPtr hDc); [DllImport("user32.dll")] public static extern IntPtr ReleaseDC(IntPtr hwnd, IntPtr hdc); [DllImport("gdi32.dll")] public static extern IntPtr CreateDIBSection(IntPtr hdc, ref Win32Types.BitmapInfo bmi, uint usage, out IntPtr ppvBits, IntPtr hSection, uint dwOffset); [DllImport("gdi32.dll")] public static extern IntPtr SelectObject(IntPtr hDc, IntPtr hObject); [DllImport("gdi32.dll")] public static extern bool DeleteObject(IntPtr hObject); [DllImport("gdi32.dll", SetLastError = true)] public static extern bool BitBlt( IntPtr hObject, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hObjectSource, int nXSrc, int nYSrc, uint dwRop); [DllImport("user32.dll")] public static extern bool PrintWindow(IntPtr hwnd, IntPtr hdcBlt, uint nFlags); } }
2、DibCaptureHelper.cs
using System; namespace CaptureSharp { internal class DibCaptureHelper { public IntPtr BitmapPtr => _hBitmap; public Win32Types.BitmapInfo BitmapInfo => _bitmapInfo; public Win32Types.Rect WindowRect => _windowRect; public Win32Types.Rect ClientRect => _clientRect; public int BitmapDataSize => _bmpDataSize; private IntPtr _hWnd = IntPtr.Zero; private IntPtr _hScrDc = IntPtr.Zero; private IntPtr _hMemDc = IntPtr.Zero; private IntPtr _hBitmap = IntPtr.Zero; private IntPtr _hOldBitmap = IntPtr.Zero; private IntPtr _bitsPtr = IntPtr.Zero; private Win32Types.BitmapInfo _bitmapInfo; private Win32Types.Rect _windowRect; private Win32Types.Rect _clientRect; private int _bmpDataSize; public bool Init(string windowName) { var handle = Win32Funcs.FindWindow(null, windowName); if (handle.Equals(IntPtr.Zero)) { return false; } return Init(handle); } public bool Init(IntPtr handle) { _hWnd = handle; //获取窗口大小 if (!Win32Funcs.GetWindowRect(_hWnd, out _windowRect) || !Win32Funcs.GetClientRect(_hWnd, out _clientRect)) { return false; } _bmpDataSize = _clientRect.Width * _clientRect.Height * 3; //位图信息 _bitmapInfo = new Win32Types.BitmapInfo {bmiHeader = new Win32Types.BitmapInfoHeader()}; _bitmapInfo.bmiHeader.Init(); _bitmapInfo.bmiHeader.biWidth = _clientRect.Width; _bitmapInfo.bmiHeader.biHeight = _clientRect.Height; _bitmapInfo.bmiHeader.biPlanes = 1; _bitmapInfo.bmiHeader.biBitCount = 24; _bitmapInfo.bmiHeader.biSizeImage = (uint) (_clientRect.Width * _clientRect.Height); _bitmapInfo.bmiHeader.biCompression = (uint) Win32Consts.BitmapCompressionMode.BI_RGB; _hScrDc = Win32Funcs.GetWindowDC(_hWnd); _hMemDc = Win32Funcs.CreateCompatibleDC(_hScrDc); _hBitmap = Win32Funcs.CreateDIBSection(_hMemDc, ref _bitmapInfo, (uint) Win32Consts.DibColorMode.DIB_RGB_COLORS, out _bitsPtr, IntPtr.Zero, 0); _hOldBitmap = Win32Funcs.SelectObject(_hMemDc, _hBitmap); return true; } public void Cleanup() { if (_hBitmap.Equals(IntPtr.Zero)) { return; } //删除用过的对象 Win32Funcs.SelectObject(_hMemDc, _hOldBitmap); Win32Funcs.DeleteObject(_hBitmap); Win32Funcs.DeleteDC(_hMemDc); Win32Funcs.ReleaseDC(_hWnd, _hScrDc); _hWnd = IntPtr.Zero; _hScrDc = IntPtr.Zero; _hMemDc = IntPtr.Zero; _hBitmap = IntPtr.Zero; _hOldBitmap = IntPtr.Zero; _bitsPtr = IntPtr.Zero; } public bool RefreshWindow() { var hWnd = _hWnd; Cleanup(); return Init(hWnd); } public bool ChangeWindowHandle(string windowName) { Cleanup(); return Init(windowName); } public bool ChangeWindowHandle(IntPtr handle) { Cleanup(); return Init(handle); } public IntPtr Capture() { if (_hBitmap.Equals(IntPtr.Zero) || _hMemDc.Equals(IntPtr.Zero) || _hScrDc.Equals(IntPtr.Zero)) { return IntPtr.Zero; } var ret = Win32Funcs.BitBlt( _hMemDc, 0, 0, _clientRect.Width, _clientRect.Height, _hScrDc, 0, 0, (uint) Win32Consts.RasterOperationMode.SRCCOPY); return ret ? _bitsPtr : IntPtr.Zero; } public bool Capture(out IntPtr bitsPtr, out int bufferSize, out Win32Types.Rect rect) { bitsPtr = _bitsPtr; bufferSize = _bmpDataSize; rect = _clientRect; if (_hBitmap.Equals(IntPtr.Zero) || _hMemDc.Equals(IntPtr.Zero) || _hScrDc.Equals(IntPtr.Zero)) { return false; } var ret = Win32Funcs.BitBlt( _hMemDc, 0, 0, _clientRect.Width, _clientRect.Height, _hScrDc, 0, 0, (uint) Win32Consts.RasterOperationMode.SRCCOPY); return ret; } } }