在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
本文基于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); } } } |
请发表评论