Public Access
122 lines
3.8 KiB
C#
122 lines
3.8 KiB
C#
using System.Drawing;
|
|
using System.Drawing.Imaging;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace MeetingAssistant.Screenshots;
|
|
|
|
public sealed class ActiveWindowScreenshotCapture : IActiveWindowScreenshotCapture
|
|
{
|
|
private static readonly IntPtr DpiAwarenessContextPerMonitorAwareV2 = new(-4);
|
|
private const int DwmWindowAttributeExtendedFrameBounds = 9;
|
|
|
|
public Task<ActiveWindowScreenshot> CaptureAsync(CancellationToken cancellationToken)
|
|
{
|
|
var previousDpiContext = TrySetThreadDpiAwarenessContext(DpiAwarenessContextPerMonitorAwareV2);
|
|
try
|
|
{
|
|
var foregroundWindow = GetForegroundWindow();
|
|
if (foregroundWindow == IntPtr.Zero ||
|
|
!TryGetWindowBounds(foregroundWindow, out var rect))
|
|
{
|
|
throw new InvalidOperationException("Could not determine the active window bounds.");
|
|
}
|
|
|
|
using var bitmap = new Bitmap(rect.Width, rect.Height);
|
|
using (var graphics = Graphics.FromImage(bitmap))
|
|
{
|
|
graphics.CopyFromScreen(rect.Left, rect.Top, 0, 0, new Size(rect.Width, rect.Height));
|
|
}
|
|
|
|
using var stream = new MemoryStream();
|
|
bitmap.Save(stream, ImageFormat.Png);
|
|
return Task.FromResult(new ActiveWindowScreenshot(stream.ToArray(), GetWindowTitle(foregroundWindow)));
|
|
}
|
|
finally
|
|
{
|
|
if (previousDpiContext != IntPtr.Zero)
|
|
{
|
|
TrySetThreadDpiAwarenessContext(previousDpiContext);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static bool TryGetWindowBounds(IntPtr window, out NativeRect rect)
|
|
{
|
|
if (DwmGetWindowAttribute(
|
|
window,
|
|
DwmWindowAttributeExtendedFrameBounds,
|
|
out rect,
|
|
Marshal.SizeOf<NativeRect>()) == 0 &&
|
|
rect.Width > 0 &&
|
|
rect.Height > 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return GetWindowRect(window, out rect) &&
|
|
rect.Width > 0 &&
|
|
rect.Height > 0;
|
|
}
|
|
|
|
private static IntPtr TrySetThreadDpiAwarenessContext(IntPtr dpiContext)
|
|
{
|
|
try
|
|
{
|
|
return SetThreadDpiAwarenessContext(dpiContext);
|
|
}
|
|
catch (EntryPointNotFoundException)
|
|
{
|
|
return IntPtr.Zero;
|
|
}
|
|
}
|
|
|
|
private static string? GetWindowTitle(IntPtr window)
|
|
{
|
|
var length = GetWindowTextLength(window);
|
|
if (length <= 0)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var buffer = new System.Text.StringBuilder(length + 1);
|
|
return GetWindowText(window, buffer, buffer.Capacity) > 0
|
|
? buffer.ToString()
|
|
: null;
|
|
}
|
|
|
|
[DllImport("user32.dll")]
|
|
private static extern IntPtr GetForegroundWindow();
|
|
|
|
[DllImport("user32.dll", SetLastError = true)]
|
|
private static extern bool GetWindowRect(IntPtr hWnd, out NativeRect lpRect);
|
|
|
|
[DllImport("dwmapi.dll", PreserveSig = true)]
|
|
private static extern int DwmGetWindowAttribute(
|
|
IntPtr hwnd,
|
|
int dwAttribute,
|
|
out NativeRect pvAttribute,
|
|
int cbAttribute);
|
|
|
|
[DllImport("user32.dll")]
|
|
private static extern IntPtr SetThreadDpiAwarenessContext(IntPtr dpiContext);
|
|
|
|
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
|
private static extern int GetWindowText(IntPtr hWnd, System.Text.StringBuilder lpString, int nMaxCount);
|
|
|
|
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
|
private static extern int GetWindowTextLength(IntPtr hWnd);
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
private readonly struct NativeRect
|
|
{
|
|
public readonly int Left;
|
|
public readonly int Top;
|
|
public readonly int Right;
|
|
public readonly int Bottom;
|
|
|
|
public int Width => Right - Left;
|
|
|
|
public int Height => Bottom - Top;
|
|
}
|
|
}
|