using System;
using System.Runtime.InteropServices;
using System.ComponentModel;
namespace PCL.Core.Utils.OS;
public static partial class WindowInterop
{
// ReSharper disable InconsistentNaming UnusedMember.Local
// DWM 外边缘结构定义
[StructLayout(LayoutKind.Sequential)]
private struct MARGINS { public int leftWidth, rightWidth, topHeight, bottomHeight; }
[LibraryImport("dwmapi.dll")]
private static partial int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS pMarInset);
[LibraryImport("dwmapi.dll")]
private static partial int DwmIsCompositionEnabled([MarshalAs(UnmanagedType.Bool)] out bool pfEnabled);
// Win32 矩形结构定义
[StructLayout(LayoutKind.Sequential)]
private struct RECT { public int left; public int top; public int right; public int bottom; }
[LibraryImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
// MONITOR_DPI_TYPE enum
private enum MONITOR_DPI_TYPE {
MDT_EFFECTIVE_DPI = 0,
MDT_ANGULAR_DPI = 1,
MDT_RAW_DPI = 2,
MDT_DEFAULT = MDT_EFFECTIVE_DPI
}
[LibraryImport("user32.dll")]
private static partial IntPtr MonitorFromWindow(IntPtr hWnd, uint dwFlags);
// Get the primary monitor handle
private const int MONITOR_DEFAULTTOPRIMARY = 1;
[LibraryImport("shcore.dll", EntryPoint = "GetDpiForMonitor")]
private static partial int GetDpiForMonitor(
IntPtr hMonitor,
MONITOR_DPI_TYPE dpiType,
out uint dpiX,
out uint dpiY
);
// ReSharper enable InconsistentNaming UnusedMember.Local
///
/// 检测 DWM 组合是否可用
///
public static bool IsCompositionEnabled()
{
var hResult = DwmIsCompositionEnabled(out var enabled);
return hResult != 0 ? throw new Win32Exception(hResult, "Failed to check DWM status") : enabled;
}
///
/// 设置 DWM 窗口边框到客户区域的扩展大小
///
public static void ExtendFrameIntoClientArea(
IntPtr hWnd, int marginLeft, int marginTop, int marginRight, int marginBottom)
{
MARGINS margins = new()
{
leftWidth = marginLeft,
rightWidth = marginRight,
topHeight = marginTop,
bottomHeight = marginBottom
};
if (!IsCompositionEnabled()) return;
var hResult = DwmExtendFrameIntoClientArea(hWnd, ref margins);
if (hResult != 0) throw new Win32Exception(hResult, "Failed to extend frame into client area");
}
///
/// See
///
public static void ExtendFrameIntoClientArea(IntPtr hWnd, int margin)
=> ExtendFrameIntoClientArea(hWnd, margin, margin, margin, margin);
///
/// 获取 Win32 窗口矩形定义
///
public static (int Left, int Top, int Right, int Bottom) GetWindowRectangle(IntPtr hWnd)
{
var hResult = GetWindowRect(hWnd, out var rect);
return hResult ? (rect.left, rect.top, rect.right, rect.bottom)
: throw new Win32Exception("Failed to get window rectangle");
}
///
/// 获取 Win32 窗口位置与大小
///
public static (int X, int Y, int Width, int Height) ToWindowBounds(
this (int Left, int Top, int Right, int Bottom) rect)
{
var (l, t, r, b) = rect;
var x = l;
var y = t;
var width = r - l;
var height = b - t;
return (x, y, width, height);
}
///
/// 获取指定屏幕的系统 DPI
///
/// 位于指定屏幕上的任意窗口句柄,默认指定主屏
public static int GetSystemDpi(IntPtr hWnd = 0) {
// Get the monitor handle
var hMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTOPRIMARY);
// 0 is S_OK
var hr = GetDpiForMonitor(hMonitor, MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI, out var dpiX, out _);
if (hr == 0)
return (int)dpiX;
// fallback to default DPI (96)
return 96;
}
}