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

 

posted @ 2020-04-27 19:44  xhubobo  阅读(3460)  评论(0编辑  收藏  举报