mirror of
https://github.com/babalae/better-genshin-impact.git
synced 2026-04-27 22:49:46 +08:00
* Refactor BitBltCapture and add BitBltSession for improved thread safety and resource management * 删掉无用的锁 * Fix session invalidation handling in BitBltCapture and add IsInvalid method to BitBltSession * Refactor resource release logic in BitBltSession to ensure proper order of operations * Refactor IsInvalid method and clean up resource release logic in BitBltSession * 析构函数加满 * 截图失败的时候重置会话避免卡死,改善锁的范围 * Use static Console for error logging in BitBltCapture * Improve error logging format in BitBltCapture for session creation failure
173 lines
4.6 KiB
C#
173 lines
4.6 KiB
C#
using System.Diagnostics;
|
|
using System.Runtime.InteropServices;
|
|
using OpenCvSharp;
|
|
using Vanara.PInvoke;
|
|
|
|
namespace Fischless.GameCapture.BitBlt;
|
|
|
|
public class BitBltSession : IDisposable
|
|
{
|
|
// 窗口句柄
|
|
private readonly HWND _hWnd;
|
|
|
|
// 来源DC
|
|
private User32.SafeReleaseHDC _hdcSrc;
|
|
|
|
// 缓冲区 CompatibleDC
|
|
private Gdi32.SafeHDC _hdcDest;
|
|
|
|
// 位图句柄
|
|
private Gdi32.SafeHBITMAP _hBitmap;
|
|
|
|
// 位图指针,这个指针会在位图释放时自动释放
|
|
private IntPtr _bitsPtr;
|
|
|
|
// 旧位图,析构时一起释放掉
|
|
private HGDIOBJ _oldBitmap;
|
|
public int Width { get; }
|
|
public int Height { get; }
|
|
|
|
private readonly object _lockObject = new object();
|
|
|
|
|
|
public BitBltSession(HWND hWnd, int w, int h)
|
|
{
|
|
lock (_lockObject)
|
|
{
|
|
try
|
|
{
|
|
if (hWnd.IsNull)
|
|
{
|
|
throw new Exception("hWnd is invalid");
|
|
}
|
|
|
|
_hWnd = hWnd;
|
|
if (w <= 0 || h <= 0)
|
|
{
|
|
throw new Exception("Invalid width or height");
|
|
}
|
|
|
|
Width = w;
|
|
Height = h;
|
|
_hdcSrc = User32.GetDC(_hWnd);
|
|
if (_hdcSrc.IsInvalid)
|
|
{
|
|
throw new Exception("Failed to get DC for {_hWnd}");
|
|
}
|
|
|
|
_hdcDest = Gdi32.CreateCompatibleDC(_hdcSrc);
|
|
if (_hdcSrc.IsInvalid)
|
|
{
|
|
Debug.Fail("Failed to create CompatibleDC");
|
|
throw new Exception("Failed to create CompatibleDC for {_hWnd}");
|
|
}
|
|
|
|
|
|
var bmi = new Gdi32.BITMAPINFO
|
|
{
|
|
bmiHeader = new Gdi32.BITMAPINFOHEADER
|
|
{
|
|
biSize = (uint)Marshal.SizeOf<Gdi32.BITMAPINFOHEADER>(),
|
|
biWidth = Width,
|
|
biHeight = -Height, // Top-down image
|
|
biPlanes = 1,
|
|
biBitCount = 32,
|
|
biCompression = 0, // BI_RGB
|
|
biSizeImage = 0
|
|
}
|
|
};
|
|
_hBitmap = Gdi32.CreateDIBSection(_hdcDest, bmi, Gdi32.DIBColorMode.DIB_RGB_COLORS, out var bits,
|
|
IntPtr.Zero);
|
|
|
|
if (_hBitmap.IsInvalid || bits == 0)
|
|
{
|
|
if (!_hBitmap.IsInvalid)
|
|
{
|
|
Gdi32.DeleteObject(_hBitmap);
|
|
}
|
|
|
|
throw new Exception($"Failed to create dIB section for {_hWnd}");
|
|
}
|
|
|
|
_bitsPtr = bits;
|
|
_oldBitmap = Gdi32.SelectObject(_hdcDest, _hBitmap);
|
|
if (_oldBitmap.IsNull)
|
|
{
|
|
throw new Exception("Failed to select object");
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
ReleaseResources();
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 调用bitblt复制并返回新的mat
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public Mat? BitBlt()
|
|
{
|
|
lock (_lockObject)
|
|
{
|
|
return Gdi32.BitBlt(_hdcDest, 0, 0, Width, Height, _hdcSrc, 0, 0, Gdi32.RasterOperationMode.SRCCOPY) ? Mat.FromPixelData(Height, Width, MatType.CV_8UC4, _bitsPtr) : null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 不是所有的失效情况都能被检测到
|
|
///
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public bool IsInvalid()
|
|
{
|
|
return _hWnd.IsNull || _hdcSrc.IsInvalid || _hdcDest.IsInvalid || _hBitmap.IsInvalid || _bitsPtr == IntPtr.Zero;
|
|
}
|
|
|
|
|
|
public void Dispose()
|
|
{
|
|
lock (_lockObject)
|
|
{
|
|
ReleaseResources();
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 需加锁环境下访问,释放资源
|
|
/// </summary>
|
|
private void ReleaseResources()
|
|
{
|
|
if (!_oldBitmap.IsNull)
|
|
{
|
|
// 先选回资源再释放_hBitmap
|
|
Gdi32.SelectObject(_hdcDest, _oldBitmap);
|
|
_oldBitmap = Gdi32.SafeHBITMAP.Null;
|
|
}
|
|
|
|
|
|
if (!_hBitmap.IsNull)
|
|
{
|
|
Gdi32.DeleteObject(_hBitmap);
|
|
_hBitmap = Gdi32.SafeHBITMAP.Null;
|
|
}
|
|
|
|
|
|
if (!_hdcDest.IsNull)
|
|
{
|
|
Gdi32.DeleteDC(_hdcDest);
|
|
_hdcDest = Gdi32.SafeHDC.Null;
|
|
}
|
|
|
|
if (_hdcSrc != IntPtr.Zero)
|
|
{
|
|
User32.ReleaseDC(_hWnd, _hdcSrc);
|
|
_hdcSrc = User32.SafeReleaseHDC.Null;
|
|
}
|
|
|
|
_bitsPtr = IntPtr.Zero;
|
|
}
|
|
} |