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

以上就是C# 抓图服务的实现的详细内容,更多关于c# 抓图服务的资料请关注我们其它相关文章!

(0)

相关推荐

  • C# 使用BitBlt进行窗口抓图的示例

    本文和C++使用BitBlt进行窗口抓图对应,使用C#实现. 这种方式对1920*1080大小的窗口,一次抓图的时间参考(VS2015+i5 9400F):低至2~3ms(平均4.3ms). 参见:C#抓图服务. 1.Win32封装 Win32Consts using System.ComponentModel; namespace CaptureSharp { public sealed class Win32Consts { public enum DibColorMode : uint {

  • C#实现通过ffmpeg从flv视频文件中截图的方法

    本文实例讲述了C#实现通过ffmpeg从flv视频文件中截图的方法.分享给大家供大家参考.具体分析如下: 需要先下载ffmpeg,这是开源的,代码如下所示: 复制代码 代码如下: using System; using System.Configuration; public class PublicMethod:System.Web.UI.Page {     public PublicMethod()     {     }     //文件路径     public static stri

  • C#实现网页截图功能

    网页截图是很常见的实用功能,今天就为大家共享一个实现浏览器截图的代码,主要程序代码如下所示: private void Form_Load(object sender, EventArgs e) { //接收web url string colle = string.Empty; string url = string.Empty; //获取进程调用传入的命令 string[] args = Environment.GetCommandLineArgs(); string[] args = ne

  • C#实现属于自己的QQ截图工具

    下面就具体介绍下实现截图工具的实现思路. 为了让大家更清楚地知道如何去实现自己的截图工具,首先我来描述下截图的一个过程--我们使用QQ的截图工具和Windows 自带的截图工具都可以发现,当我们点击QQ窗体中的截图按钮时,此时我们将看到一个全屏图片,然后我们可以在其上截图,当鼠标左键按下时,即代表开始截图,并我们可以移动鼠标来改变截图的大小,鼠标弹起时即代表结束截图,此时我们可以双击矩形区域完全截图,并且可以通过粘贴操作把截取的图片粘贴到聊天窗口的发送区,鼠标右键点击则是退出截图.这样我们截图的

  • C# 实现SDL2进行视频播放窗口截图和字幕添加

    使用SDL2进行视频播放窗口截图和字幕添加操作 SDL API查看:https://wiki.libsdl.org/APIByCategory 视频截图 我就废话不多说了,大家还是直接看代码吧~ /// <summary> /// SDL2截图操作类 /// </summary> public unsafe class SDLScreenshot { IntPtr window;// 窗口对象 IntPtr renderer;// 播放窗口的渲染器(来自于已初始化的播放窗口渲染器)

  • C#实现的滚动网页截图功能示例

    本文实例讲述了C#实现的滚动网页截图功能.分享给大家供大家参考,具体如下: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace WindowsFormsApplic

  • C# 实现截图软件功能实例代码

    本文是利用C# 开发截图软件的小例子,以供学习分享使用. 思路: 截取屏幕图片. 获取要截取的范围,即左上角,右下角坐标 填充到PictureBox中. 笔触功能,荧光笔,矩形,橡皮擦,复制,保存功能 涉及的知识点: MenuStrip:为窗体提供菜单系统.以ToolStripMenuItem为菜单子选项 ToolStrip:为 Windows 工具栏对象提供容器.以ToolStripButton[表示包含文本和图像的可选]为工具栏子元素 PictureBox:表示用于显示图像的 Windows

  • c#实现winform屏幕截图并保存的示例

    复制代码 代码如下: using System.Runtime.InteropServices;using System.Drawing.Imaging;    [System.Runtime.InteropServices.DllImportAttribute("gdi32.dll")]    private static extern bool BitBlt( IntPtr hdcDest,   //   目标   DC的句柄                       int n

  • C#实现QQ截图功能及相关问题

    对于QQ截图,肯定是早就有认识了,只是一直没有去认真观察这个操作的具体实现步骤.所以这里将自己的记忆中的步骤简单的写一下: 习惯性用QQ或者TIM的人,一般是使用Ctrl+Alt+A  快捷键(热键)快速实现截图. Ctrl+Alt+A  进入截图模式 鼠标左键点击 鼠标拖动对截图去进行选取 鼠标左键弹起 双击截图区域  保存图片到剪贴板 鼠标右键点击 退出截图模式 因为考虑到截图模式的时候  一般只能显示一个窗体  所以就考虑使用单例模式  在ScreenBody窗体中实现以下代码 1:创建单

  • C#实现在网页中根据url截图并输出到网页的方法

    本文实例讲述了C#实现在网页中根据url截图并输出到网页的方法.分享给大家供大家参考,具体如下: 网页截图是很多站点的一个小需求,这段代码实现的是如何根据url获得网页截图并输出到网页中. using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.Thr

  • C# 实现QQ式截图功能实例代码

    这个功能一共有两部分组成,第一部分是窗体代码,另外的一部分是一个辅助方法.直接贴出代码,以供大家参考: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.D

随机推荐