C# 抓圖服務的實現
阿新 • • 發佈:2021-01-08
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 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,WinClientRect.Width,WinClientRect.Height,HScrDc,(uint) Win32Consts.RasterOperationMode.SRCCOPY); return ret ? _bitsPtr : IntPtr.Zero; } protected override bool DoCapture(out IntPtr bitsPtr) { bitsPtr = _bitsPtr; var ret = Win32Funcs.BitBltWrapper( HMemDc,(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.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,(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成功,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成功,out IntPtr bitsPtr,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,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,bitmapInfo); } } }
以上就是C# 抓圖服務的實現的詳細內容,更多關於c# 抓圖服務的資料請關注我們其它相關文章!