Windows监视系统的缩放

using System.Runtime.InteropServices;
using System.Text;

internal static class Program
{
    private static void Main()
    {
        Console.OutputEncoding = Encoding.UTF8;
        SetProcessDpiAwareness(2); // PROCESS_PER_MONITOR_DPI_AWARE

        Dictionary<string, uint> previous = ReadScales();
        foreach ((string name, uint dpi) in previous)
            Console.WriteLine($"{name}:{ToPercent(dpi):F0}%");

        using var changed = new AutoResetEvent(false);
        Marshal.ThrowExceptionForHR(RegisterScaleChangeEvent(
            changed.SafeWaitHandle.DangerousGetHandle(), out _));

        while (changed.WaitOne())
        {
            //Thread.Sleep(200);
            Dictionary<string, uint> current = ReadScales();

            foreach ((string name, uint dpi) in current)
            {
                if (previous.TryGetValue(name, out uint oldDpi) && oldDpi != dpi)
                    Console.WriteLine(
                        $"[{DateTime.Now:HH:mm:ss.fff}] {name}:"
                        + $"{ToPercent(oldDpi):F0}% -> {ToPercent(dpi):F0}%");
            }

            previous = current;
        }
    }

    private static Dictionary<string, uint> ReadScales()
    {
        var result = new Dictionary<string, uint>(StringComparer.OrdinalIgnoreCase);

        EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, (monitor, _, _, _) =>
        {
            var info = new MonitorInfo { Size = (uint)Marshal.SizeOf<MonitorInfo>() };
            GetMonitorInfo(monitor, ref info);
            Marshal.ThrowExceptionForHR(GetDpiForMonitor(monitor, 0, out uint dpi, out _));
            result[info.DeviceName] = dpi;
            return true;
        }, IntPtr.Zero);

        return result;
    }

    private static double ToPercent(uint dpi) => dpi * 100.0 / 96;

    [StructLayout(LayoutKind.Sequential)]
    private struct Rect { public int Left, Top, Right, Bottom; }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private struct MonitorInfo
    {
        public uint Size;
        public Rect Monitor, WorkArea;
        public uint Flags;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string DeviceName;
    }

    private delegate bool MonitorCallback(IntPtr monitor, IntPtr dc, IntPtr rect, IntPtr data);

    [DllImport("user32.dll")]
    private static extern bool EnumDisplayMonitors(
        IntPtr dc, IntPtr clip, MonitorCallback callback, IntPtr data);

    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    private static extern bool GetMonitorInfo(IntPtr monitor, ref MonitorInfo info);

    [DllImport("shcore.dll")]
    private static extern int SetProcessDpiAwareness(int awareness);

    [DllImport("shcore.dll")]
    private static extern int GetDpiForMonitor(
        IntPtr monitor, int type, out uint dpiX, out uint dpiY);

    [DllImport("shcore.dll")]
    private static extern int RegisterScaleChangeEvent(
        IntPtr eventHandle, out nuint registrationCookie);
}


h2

注意如果是WPF程序需要增加app.manifest

  <?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
      <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
    </windowsSettings>
  </application>
</assembly>

注意
true/pm
PerMonitorV2

posted @ 2026-08-18 17:32  stweily  阅读(5)  评论(0)    收藏  举报