using System.Runtime.InteropServices; namespace CursorLang.Interop; /// /// Win32 API для размещения окна настроек: его собственные границы и рабочая /// область монитора, на который окно просят поставить. /// /// /// Границы берутся у системы, а не из Window.Left/Top/Width/Height: /// высота окна подстраивается под содержимое, и WPF пересчитывает эти свойства /// по DPI монитора, а рабочая область монитора приходит в пикселях. Считать /// центр в одних единицах проще, чем переводить туда-обратно. /// internal static class WindowPlacementNative { [DllImport("user32.dll")] private static extern bool GetWindowRect(IntPtr hWnd, out PopupWindowNative.Rect lpRect); [DllImport("user32.dll")] private static extern IntPtr MonitorFromRect(ref PopupWindowNative.Rect lprc, uint dwFlags); [DllImport("user32.dll")] private static extern bool GetMonitorInfo(IntPtr hMonitor, ref MonitorInfo lpmi); [StructLayout(LayoutKind.Sequential)] private struct MonitorInfo { public int cbSize; public PopupWindowNative.Rect rcMonitor; public PopupWindowNative.Rect rcWork; public uint dwFlags; } private const uint MONITOR_DEFAULTTONEAREST = 2; /// Границы окна в пикселях экрана — вместе с рамкой и заголовком. internal static PopupWindowNative.Rect? TryGetBounds(IntPtr hWnd) => GetWindowRect(hWnd, out PopupWindowNative.Rect bounds) ? bounds : null; /// /// Рабочая область — без панели задач — того монитора, на котором /// прямоугольник находится целиком или хотя бы большей частью. /// internal static PopupWindowNative.Rect? TryGetWorkAreaNear(PopupWindowNative.Rect rect) { IntPtr monitor = MonitorFromRect(ref rect, MONITOR_DEFAULTTONEAREST); var info = new MonitorInfo { cbSize = Marshal.SizeOf() }; return GetMonitorInfo(monitor, ref info) ? info.rcWork : null; } }