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