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