C# 抓图服务的实现

作者:xhubobo 时间:2023-04-03 20:10:12 

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

来源:https://www.cnblogs.com/xhubobo/p/12809988.html

标签:c#,抓图
0
投稿

猜你喜欢

  • 解决CollectionUtils.isNotEmpty()不存在的问题

    2021-11-15 12:03:17
  • 深入解析C#编程中struct所定义的结构

    2022-03-01 12:43:08
  • MyBatis实现物理分页的实例

    2023-03-13 04:21:45
  • java日志打印的完全使用指南

    2023-07-02 15:02:28
  • java显示当前运行时的参数(java运行参数)

    2023-09-07 10:03:22
  • C#缩略图多路径多格式保存的实例

    2021-10-29 18:12:36
  • 一文带你入门SpringMVC的配置与使用

    2021-06-24 12:30:52
  • Mybatis Plugin拦截器开发过程详解

    2021-07-15 22:30:18
  • SpringBoot中定位切点的两种常用方法

    2023-09-26 08:45:17
  • 轻量级声明式的Http库——Feign的独立使用

    2022-06-05 04:36:19
  • 详解Java设计模式编程中的访问者模式

    2023-11-28 01:00:43
  • Java使用HttpClient实现Post请求实例

    2022-05-22 08:37:43
  • 详解IDEA的快捷键及智能提示

    2023-10-18 07:27:03
  • java实现猜拳游戏

    2023-08-04 04:23:52
  • Java实现限定时间CountDownLatch并行场景

    2023-06-05 01:47:27
  • Java日常练习题,每天进步一点点(32)

    2022-04-29 07:12:53
  • Jmeter 中 CSV 如何参数化测试数据并实现自动断言示例详解

    2023-10-17 15:37:42
  • JPA @Basic单表查询如何实现大字段懒加载

    2021-06-03 18:02:00
  • linux(center OS7)安装JDK、tomcat、mysql 搭建java web项目运行环境

    2022-07-04 07:02:37
  • Android Studio 运行按钮灰色的完美解决方法

    2023-08-16 05:59:42
  • asp之家 软件编程 m.aspxhome.com