C# 抓图服务的实现
作者:xhubobo 发布时间:2023-04-03 20:10:12
标签: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);
}
}
}
来源:https://www.cnblogs.com/xhubobo/p/12809988.html


猜你喜欢
- 本文实例为大家分享了Java实现五子棋游戏的具体代码,供大家参考,具体内容如下学习目的:熟悉java中swing类与java基础知识的巩固.
- 前言:如果简单地拍照片并非您应用的主要目标,那么您可能希望从相机应用中获取图片并对该图片执行一些操作。一、这就是第一种方法,比较简单,不用将
- TabLayout和ViewPager搭配使用,是有很多方便性,但是TabLayout这东西还是有很多被人吐槽的地方。这里只讲怎么设置tab
- 前提:① 已经提供了一个wsdl接口② 该接口能正常调用总体分为两种方式:1.使用cxf的wsdl2java工具生成本地类(使用方式就是本地
- 封装(Encapsulation)是面向对象编程的一个核心概念,它意味着将数据(属性)和方法(操作数据的函数)捆绑在一起,形成一个类(Cla
- 1.为什么要用thrift js C#? 1.1 首先,js 通过 thrift 访问C#,实际上是一种
- 本文实例讲述了Android编程使WebView支持HTML5 Video全屏播放的解决方法。分享给大家供大家参考,具体如下:1)需要在An
- 目录开始准备开始动画画圆弧项目使用背景图完整代码今天介绍一个很简单的倒计时动画,仿酷狗音乐的启动页倒计时效果,也是大多数APP在用的一个动画
- 使用例子如下,用JAVA 运行Sort1, 输入你要排序的文件路径 , 如 例子是对H:\下的文件和它所有子文件夹下的文件进行排序2, 输入
- 一、系统介绍1.开发环境开发工具:Eclipse2021JDK版本:jdk1.8Mysql版本:8.0.132.技术选型Java+Swing
- 近来复习数据结构,自己动手实现了栈。栈是一种限制插入和删除只能在一个位置上的表。最基本的操作是进栈和出栈,因此,又被叫作“先进后出”表。首先
- Java 线程类也是一个 object 类,它的实例都继承自 java.lang.Thread 或其子类。 可以用如下方式用 java 中创
- 尽管Java提供了一个可以处理文件的IO操作类。 但是没有一个复制文件的方法。 复制文件是一个重要的操作,当你的程序必须处理很多文件相关的时
- 1.依赖maven依赖如下,需要说明的是,spring-boot-starter-data-redis里默认是使用lettuce作为redi
- java 进制转换实例详解十进制转成十六进制:  
- 介绍一款简洁实用的图片编辑器,纯dart开发。支持:涂鸦、旋转&翻转、马赛克、添加文字,及自定义ui风格。功能演示涂鸦旋转&
- Mybatis注解开发单表操作MyBatis的常用注解Mybatis也可以使用注解开发方式,这样我们就可以减少编写Mapper映射文件了。我
- 多个条件使用Map传递参数进行批量删除1、使用场景因为项目需要针对资源文件(视频、音频、文档),编辑时候可能出现以下3种情况:实现的项目效果
- 本文实例讲述了C#中DataGridView常用操作。分享给大家供大家参考。具体如下:public void Binder1(){ Data
- C#事件使用+= -=使用起来是很方便的,但是却不能整体清空所有事件。比如一个常见的操作,打开界面注册监听事件,关闭界面需要把所有的事件清空