• C#抓图服务


    本文基于C#使用BitBlt进行窗口抓图C#使用PrintWindow进行窗口抓图,和C++抓图服务对应。

    C#抓图服务首先抽象出抓图接口,然后对接口做基于公共操作的抽象类封装,之后针对不同的抓图方式做差异化处理,最后根据接口实现抓图服务。

    注意:Win32封装实现参考C#使用BitBlt进行窗口抓图

    Github示例工程:SimpleWindowCapture

    1、抓图接口

    using System;
    using Win32Proxy;
    
    namespace CaptureProxy
    {
        internal interface ICaptureHelper
        {
            bool Init(string windowName);
            bool Init(IntPtr handle);
            void Cleanup();
            bool RefreshWindow();
            bool ChangeWindowHandle(string windowName);
            bool ChangeWindowHandle(IntPtr handle);
            IntPtr Capture();
            bool Capture(out IntPtr bitsPtr, out int bufferSize, out Win32Types.Rect rect);
    
            Win32Types.Rect WindowRect { get; }
            Win32Types.Rect ClientRect { get; }
            int BitmapDataSize { get; }
            IntPtr BitmapPtr { get; }
            Win32Types.BitmapInfo BitmapInfo { get; }
        }
    }
    using System.ComponentModel;
    
    namespace CaptureProxy
    {
        public enum CaptureType
        {
            [Description("使用CreateDIBSection抓图,速度快,但是无法抓取D3D等渲染的窗口")]
            CreateDibSection = 0,
    
            [Description("使用PrintWindow抓图,速度慢(16ms左右),但是可以抓取D3D等渲染的窗口")]
            PrintWindow
        }
    }

    2、抓图抽象类

    using System;
    using Win32Proxy;
    
    namespace CaptureProxy
    {
        internal abstract class AbsCaptureHelper : ICaptureHelper
        {
            public Win32Types.Rect WindowRect => _windowRect;
            public Win32Types.Rect ClientRect => WinClientRect;
            public int BitmapDataSize => _bmpDataSize;
            public IntPtr BitmapPtr => HBitmap;
            public Win32Types.BitmapInfo BitmapInfo { get; } = new Win32Types.BitmapInfo();
    
            protected IntPtr HWnd = IntPtr.Zero;
            protected IntPtr HScrDc = IntPtr.Zero;
            protected IntPtr HMemDc = IntPtr.Zero;
            protected IntPtr HBitmap = IntPtr.Zero;
            protected IntPtr HOldBitmap = IntPtr.Zero;
    
            private Win32Types.Rect _windowRect;
            protected Win32Types.Rect WinClientRect;
            private int _bmpDataSize;
    
            protected abstract bool CommonInit();
            protected abstract IntPtr DoCapture();
            protected abstract bool DoCapture(out IntPtr bitsPtr);
    
            public bool Init(string windowName)
            {
                var handle = Win32Funcs.FindWindowWrapper(null, windowName);
                if (handle.Equals(IntPtr.Zero))
                {
                    return false;
                }
    
                return Init(handle);
            }
    
            public bool Init(IntPtr handle)
            {
                HWnd = handle;
    
                //获取窗口大小
                if (!Win32Funcs.GetWindowRectWrapper(HWnd, out _windowRect)
                    || !Win32Funcs.GetClientRectWrapper(HWnd, out WinClientRect))
                {
                    return false;
                }
    
                _bmpDataSize = WinClientRect.Width * WinClientRect.Height * 3;
    
                return CommonInit();
            }
    
            public void Cleanup()
            {
                if (HBitmap.Equals(IntPtr.Zero))
                {
                    return;
                }
    
                //删除用过的对象
                Win32Funcs.SelectObjectWrapper(HMemDc, HOldBitmap);
                Win32Funcs.DeleteObjectWrapper(HBitmap);
                Win32Funcs.DeleteDcWrapper(HMemDc);
                Win32Funcs.ReleaseDcWrapper(HWnd, HScrDc);
    
                HWnd = IntPtr.Zero;
                HScrDc = IntPtr.Zero;
                HMemDc = IntPtr.Zero;
                HBitmap = IntPtr.Zero;
                HOldBitmap = IntPtr.Zero;
                //_bitsPtr = IntPtr.Zero;
            }
    
            public bool RefreshWindow()
            {
                return ChangeWindowHandle(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;
                }
    
                return DoCapture();
            }
    
            public bool Capture(out IntPtr bitsPtr, out int bufferSize, out Win32Types.Rect rect)
            {
                bitsPtr = IntPtr.Zero;
                bufferSize = _bmpDataSize;
                rect = WinClientRect;
    
                if (HBitmap.Equals(IntPtr.Zero) || HMemDc.Equals(IntPtr.Zero) || HScrDc.Equals(IntPtr.Zero))
                {
                    return false;
                }
    
                return DoCapture(out bitsPtr);
            }
        }
    }

    3、抓图类实现

    using System;
    using Win32Proxy;
    
    namespace CaptureProxy
    {
        internal class DibCaptureHelper : AbsCaptureHelper
        {
            private Win32Types.BitmapInfo _bitmapInfo;
            private IntPtr _bitsPtr = IntPtr.Zero;
    
            protected override bool CommonInit()
            {
                //位图信息
                _bitmapInfo = new Win32Types.BitmapInfo {bmiHeader = new Win32Types.BitmapInfoHeader()};
                _bitmapInfo.bmiHeader.Init();
                _bitmapInfo.bmiHeader.biWidth = WinClientRect.Width;
                _bitmapInfo.bmiHeader.biHeight = WinClientRect.Height;
                _bitmapInfo.bmiHeader.biPlanes = 1;
                _bitmapInfo.bmiHeader.biBitCount = 24;
                _bitmapInfo.bmiHeader.biSizeImage = (uint) (WinClientRect.Width * WinClientRect.Height);
                _bitmapInfo.bmiHeader.biCompression = (uint) Win32Consts.BitmapCompressionMode.BI_RGB;
    
                HScrDc = Win32Funcs.GetWindowDcWrapper(HWnd);
                HMemDc = Win32Funcs.CreateCompatibleDcWrapper(HScrDc);
                HBitmap = Win32Funcs.CreateDibSectionWrapper(HMemDc, ref _bitmapInfo,
                    (uint) Win32Consts.DibColorMode.DIB_RGB_COLORS,
                    out _bitsPtr, IntPtr.Zero, 0);
                HOldBitmap = Win32Funcs.SelectObjectWrapper(HMemDc, HBitmap);
    
                return true;
            }
    
            protected override IntPtr DoCapture()
            {
                var ret = Win32Funcs.BitBltWrapper(
                    HMemDc, 0, 0, WinClientRect.Width, WinClientRect.Height,
                    HScrDc, 0, 0,
                    (uint) Win32Consts.RasterOperationMode.SRCCOPY);
                return ret ? _bitsPtr : IntPtr.Zero;
            }
    
            protected override bool DoCapture(out IntPtr bitsPtr)
            {
                bitsPtr = _bitsPtr;
                var ret = Win32Funcs.BitBltWrapper(
                    HMemDc, 0, 0, WinClientRect.Width, WinClientRect.Height,
                    HScrDc, 0, 0,
                    (uint) Win32Consts.RasterOperationMode.SRCCOPY);
                return ret;
            }
        }
    }
    using System;
    using Win32Proxy;
    
    namespace CaptureProxy
    {
        internal class PrintCaptureHelper : AbsCaptureHelper
        {
            protected override bool CommonInit()
            {
                HScrDc = Win32Funcs.GetWindowDcWrapper(HWnd);
                HBitmap = Win32Funcs.CreateCompatibleBitmapWrapper(HScrDc, WinClientRect.Width, WinClientRect.Height);
                HMemDc = Win32Funcs.CreateCompatibleDcWrapper(HScrDc);
                HOldBitmap = Win32Funcs.SelectObjectWrapper(HMemDc, HBitmap);
                return true;
            }
    
            protected override IntPtr DoCapture()
            {
                var ret = Win32Funcs.PrintWindowWrapper(HWnd, HMemDc,
                    (uint) Win32Consts.PrintWindowMode.PW_CLIENTONLY |
                    (uint) Win32Consts.PrintWindowMode.PW_RENDERFULLCONTENT);
                return ret ? HBitmap : IntPtr.Zero;
            }
    
            protected override bool DoCapture(out IntPtr bitsPtr)
            {
                bitsPtr = HBitmap;
                var ret = Win32Funcs.PrintWindowWrapper(HWnd, HMemDc,
                    (uint) Win32Consts.PrintWindowMode.PW_CLIENTONLY |
                    (uint) Win32Consts.PrintWindowMode.PW_RENDERFULLCONTENT);
                return ret;
            }
        }
    }

    4、抓图服务

    using System;
    using System.Collections.Generic;
    using Win32Proxy;
    
    namespace CaptureProxy
    {
        public class CaptureService
        {
            private readonly Dictionary<string, ICaptureHelper> _dicCaptureHelper;
    
            /// <summary>
            /// 注册抓图服务
            /// </summary>
            /// <param name="name">抓图服务名称</param>
            /// <param name="windowName">窗口名称</param>
            /// <param name="type">抓图类型</param>
            /// <returns>true成功,false失败</returns>
            public bool RegisterCapture(string name, string windowName, CaptureType type = CaptureType.CreateDibSection)
            {
                if (string.IsNullOrEmpty(name) || _dicCaptureHelper.ContainsKey(name))
                {
                    return false;
                }
    
                ICaptureHelper helper;
                switch (type)
                {
                    case CaptureType.CreateDibSection:
                        helper = new DibCaptureHelper();
                        break;
                    case CaptureType.PrintWindow:
                        helper = new PrintCaptureHelper();
                        break;
                    default:
                        return false;
                }
    
                if (!helper.Init(windowName))
                {
                    return false;
                }
    
                _dicCaptureHelper.Add(name, helper);
    
                return true;
            }
    
            /// <summary>
            /// 注册抓图服务
            /// </summary>
            /// <param name="name">抓图服务名称</param>
            /// <param name="handle">窗口句柄</param>
            /// <param name="type">抓图类型</param>
            /// <returns>true成功,false失败</returns>
            public bool RegisterCapture(string name, IntPtr handle, CaptureType type = CaptureType.CreateDibSection)
            {
                if (string.IsNullOrEmpty(name) || _dicCaptureHelper.ContainsKey(name))
                {
                    return false;
                }
    
                ICaptureHelper helper;
                switch (type)
                {
                    case CaptureType.CreateDibSection:
                        helper = new DibCaptureHelper();
                        break;
                    case CaptureType.PrintWindow:
                        helper = new PrintCaptureHelper();
                        break;
                    default:
                        return false;
                }
    
                if (!helper.Init(handle))
                {
                    return false;
                }
    
                _dicCaptureHelper.Add(name, helper);
    
                return true;
            }
    
            /// <summary>
            /// 获取是否已注册抓图服务
            /// </summary>
            /// <param name="name">抓图服务名称</param>
            /// <returns>true已注册,false未注册</returns>
            public bool IsRegister(string name)
            {
                return !string.IsNullOrEmpty(name) && _dicCaptureHelper.ContainsKey(name);
            }
    
            /// <summary>
            /// 注销抓图服务
            /// </summary>
            /// <param name="name">抓图服务名称</param>
            public void UnRegisterCapture(string name)
            {
                if (string.IsNullOrEmpty(name) || !_dicCaptureHelper.ContainsKey(name))
                {
                    return;
                }
    
                _dicCaptureHelper[name]?.Cleanup();
                _dicCaptureHelper.Remove(name);
            }
    
            /// <summary>
            /// 清理所有抓图服务
            /// </summary>
            public void Cleanup()
            {
                foreach (var helper in _dicCaptureHelper)
                {
                    helper.Value?.Cleanup();
                }
    
                _dicCaptureHelper.Clear();
            }
    
            public bool RefreshWindow(string name)
            {
                var ret = !string.IsNullOrEmpty(name) && _dicCaptureHelper.ContainsKey(name);
                if (ret)
                {
                    ret = _dicCaptureHelper[name].RefreshWindow();
                }
    
                return ret;
            }
    
            /// <summary>
            /// 修改窗口句柄
            /// </summary>
            /// <param name="name">抓图服务名称</param>
            /// <param name="handle">窗口句柄</param>
            public bool ChangeWindowHandle(string name, IntPtr handle)
            {
                return !string.IsNullOrEmpty(name) && _dicCaptureHelper.ContainsKey(name) &&
                       _dicCaptureHelper[name].ChangeWindowHandle(handle);
            }
    
            /// <summary>
            /// 修改窗口句柄
            /// </summary>
            /// <param name="name">抓图服务名称</param>
            /// <param name="windowName">窗口名称</param>
            public bool ChangeWindowHandle(string name, string windowName)
            {
                return !string.IsNullOrEmpty(name) && _dicCaptureHelper.ContainsKey(name) &&
                       _dicCaptureHelper[name].ChangeWindowHandle(windowName);
            }
    
            /// <summary>
            /// 获取窗口尺寸
            /// </summary>
            /// <param name="name">抓图服务名称</param>
            /// <param name="winRect">输出的窗口尺寸</param>
            /// <returns>true成功,false失败</returns>
            public bool GetWindowRect(string name, out Win32Types.Rect winRect)
            {
                var ret = !string.IsNullOrEmpty(name) && _dicCaptureHelper.ContainsKey(name);
                winRect = ret ? _dicCaptureHelper[name].WindowRect : new Win32Types.Rect();
                return ret;
            }
    
            /// <summary>
            /// 获取窗口客户区尺寸
            /// </summary>
            /// <param name="name">抓图服务名称</param>
            /// <param name="clientRect">输出的窗口客户区尺寸</param>
            /// <returns>true成功,false失败</returns>
            public bool GetClientRect(string name, out Win32Types.Rect clientRect)
            {
                var ret = !string.IsNullOrEmpty(name) && _dicCaptureHelper.ContainsKey(name);
                clientRect = ret ? _dicCaptureHelper[name].ClientRect : new Win32Types.Rect();
                return ret;
            }
    
            /// <summary>
            /// 获取抓图数据大小
            /// </summary>
            /// <param name="name">抓图服务名称</param>
            /// <param name="bmpDataSize">抓图数据大小</param>
            /// <returns>true成功,false失败</returns>
            public bool GetBitmapDataSize(string name, out int bmpDataSize)
            {
                var ret = !string.IsNullOrEmpty(name) && _dicCaptureHelper.ContainsKey(name);
                bmpDataSize = ret ? _dicCaptureHelper[name].BitmapDataSize : 0;
                return ret;
            }
    
            public IntPtr GetBitmapPtr(string name)
            {
                if (string.IsNullOrEmpty(name) || !_dicCaptureHelper.ContainsKey(name))
                {
                    return IntPtr.Zero;
                }
    
                return _dicCaptureHelper[name].BitmapPtr;
            }
    
            public Win32Types.BitmapInfo GetBitmapInfo(string name)
            {
                if (string.IsNullOrEmpty(name) || !_dicCaptureHelper.ContainsKey(name))
                {
                    return new Win32Types.BitmapInfo();
                }
    
                return _dicCaptureHelper[name].BitmapInfo;
            }
    
            /// <summary>
            /// 获取抓图
            /// </summary>
            /// <param name="name">抓图服务名称</param>
            /// <param name="bitsPtr">位图指针</param>
            /// <returns>true成功,false失败</returns>
            public bool Capture(string name, out IntPtr bitsPtr)
            {
                var ret = !string.IsNullOrEmpty(name) && _dicCaptureHelper.ContainsKey(name);
                bitsPtr = ret ? _dicCaptureHelper[name].Capture() : IntPtr.Zero;
                return ret && !bitsPtr.Equals(IntPtr.Zero);
            }
    
            /// <summary>
            /// 获取抓图
            /// </summary>
            /// <param name="name">抓图服务名称</param>
            /// <param name="bitsPtr">位图指针</param>
            /// <param name="bufferSize">位图数据大小</param>
            /// <param name="texSize">位图尺寸</param>
            /// <returns>true成功,false失败</returns>
            public bool Capture(string name, out IntPtr bitsPtr, out int bufferSize, out Win32Types.Rect texSize)
            {
                if (string.IsNullOrEmpty(name) || !_dicCaptureHelper.ContainsKey(name))
                {
                    bitsPtr = IntPtr.Zero;
                    bufferSize = 0;
                    texSize = new Win32Types.Rect();
                    return false;
                }
    
                return _dicCaptureHelper[name].Capture(out bitsPtr, out bufferSize, out texSize);
            }
    
            #region 单例模式
    
            private static CaptureService _instance;
    
            private static readonly object LockHelper = new object();
    
            private CaptureService()
            {
                _dicCaptureHelper = new Dictionary<string, ICaptureHelper>();
            }
    
            public static CaptureService Instance
            {
                get
                {
                    if (_instance != null)
                    {
                        return _instance;
                    }
    
                    lock (LockHelper)
                    {
                        _instance = _instance ?? new CaptureService();
                    }
    
                    return _instance;
                }
            }
    
            #endregion
        }
    }

    5、使用示例

    using System;
    using System.Threading;
    using CaptureProxy;
    using Win32Proxy;
    
    namespace SimpleWindowCapture
    {
        internal sealed class CaptureHelper
        {
            public event Action<string, IntPtr, Win32Types.BitmapInfo> CaptureDone =
                (captureName, bitmapPtr, bitmapInfo) => { };
    
            public int Fps { get; set; } = 15;
    
            private double TimerInterval => 1000.0 / Fps;
            private string _captureName;
            private Timer _timer;
    
            public bool Start(string captureName, IntPtr handle, CaptureType type = CaptureType.CreateDibSection)
            {
                if (!CaptureService.Instance.RegisterCapture(captureName, handle, type))
                {
                    return false;
                }
    
                _captureName = captureName;
    
                //创建守护定时器,马上执行
                _timer = new Timer(CaptureFunc, null,
                    TimeSpan.FromMilliseconds(0), Timeout.InfiniteTimeSpan);
    
                return true;
            }
    
            public void Stop()
            {
                //移除定时器
                _timer?.Dispose();
                _timer = null;
    
                CaptureService.Instance.UnRegisterCapture(_captureName);
                _captureName = string.Empty;
            }
    
            private void CaptureFunc(object state)
            {
                Capture();
    
                //执行下次定时器
                _timer?.Change(TimeSpan.FromMilliseconds(TimerInterval), Timeout.InfiniteTimeSpan);
            }
    
            private void Capture()
            {
                IntPtr bitsPtr;
                if (!CaptureService.Instance.Capture(_captureName, out bitsPtr))
                {
                    return;
                }
    
                var bitmapPtr = CaptureService.Instance.GetBitmapPtr(_captureName);
                var bitmapInfo = CaptureService.Instance.GetBitmapInfo(_captureName);
                CaptureDone.Invoke(_captureName, bitmapPtr, bitmapInfo);
            }
        }
    }
  • 相关阅读:
    阿里云服务器,http免费转https详细教程
    springboot系列总结(二)---springboot的常用注解
    springboot系列总结(一)---初识springboot
    java 查询当天0点0分0秒
    mysql各个版本驱动jar包下载 mysql/mysql-connector-java/5.1.22
    泰坦尼克 学习
    切片
    忽略warning 警告
    迭代 递归 循环 遍历
    标准化 归一化
  • 原文地址:https://www.cnblogs.com/xhubobo/p/12809988.html
Copyright © 2020-2023  润新知