125 lines
4.2 KiB
C#
125 lines
4.2 KiB
C#
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
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 检测 DWM 组合是否可用
|
||
|
|
/// </summary>
|
||
|
|
public static bool IsCompositionEnabled()
|
||
|
|
{
|
||
|
|
var hResult = DwmIsCompositionEnabled(out var enabled);
|
||
|
|
return hResult != 0 ? throw new Win32Exception(hResult, "Failed to check DWM status") : enabled;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 设置 DWM 窗口边框到客户区域的扩展大小
|
||
|
|
/// </summary>
|
||
|
|
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");
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// See <see cref="ExtendFrameIntoClientArea(IntPtr, int, int, int, int)"/>
|
||
|
|
/// </summary>
|
||
|
|
public static void ExtendFrameIntoClientArea(IntPtr hWnd, int margin)
|
||
|
|
=> ExtendFrameIntoClientArea(hWnd, margin, margin, margin, margin);
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 获取 Win32 窗口矩形定义
|
||
|
|
/// </summary>
|
||
|
|
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");
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 获取 Win32 窗口位置与大小
|
||
|
|
/// </summary>
|
||
|
|
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);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 获取指定屏幕的系统 DPI
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="hWnd">位于指定屏幕上的任意窗口句柄,默认指定主屏</param>
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
}
|