Files
meeting-assistant/MeetingAssistant/Screenshots/ActiveWindowScreenshotCapture.Windows.cs

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