1. 程式人生 > 程式設計 >C# 抓圖服務的實現

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 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# 抓圖服務的資料請關注我們其它相關文章!