From 963453a1ff9341b82f0546586791f33856097e5b Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 24 Oct 2018 08:24:12 +0200 Subject: [PATCH 1/2] Size window in response to WM_DPICHANGED. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 45120fa21b..d78c213299 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -497,9 +497,15 @@ namespace Avalonia.Win32 case UnmanagedMethods.WindowsMessage.WM_DPICHANGED: var dpi = ToInt32(wParam) & 0xffff; var newDisplayRect = Marshal.PtrToStructure(lParam); - Position = new Point(newDisplayRect.left, newDisplayRect.top); _scaling = dpi / 96.0; ScalingChanged?.Invoke(_scaling); + SetWindowPos(hWnd, + IntPtr.Zero, + newDisplayRect.left, + newDisplayRect.top, + newDisplayRect.right - newDisplayRect.left, + newDisplayRect.bottom - newDisplayRect.top, + SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE); return IntPtr.Zero; case UnmanagedMethods.WindowsMessage.WM_KEYDOWN: From 787f3ae1fcc5f67297588937b2c0651a099c119c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 24 Oct 2018 09:22:42 +0200 Subject: [PATCH 2/2] Try all the different methods to set DPI awareness. Don't rely on `Environment.OSVersion.Version` as this value lies. --- .../Interop/UnmanagedMethods.cs | 12 +++++- src/Windows/Avalonia.Win32/Win32Platform.cs | 43 +++++++++++++------ 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index c3c867d3fb..9597e1fd78 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -31,6 +31,11 @@ namespace Avalonia.Win32.Interop public delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); + public static readonly IntPtr DPI_AWARENESS_CONTEXT_UNAWARE = new IntPtr(-1); + public static readonly IntPtr DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = new IntPtr(-2); + public static readonly IntPtr DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = new IntPtr(-3); + public static readonly IntPtr DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = new IntPtr(-4); + public enum Cursor { IDC_ARROW = 32512, @@ -956,6 +961,9 @@ namespace Avalonia.Win32.Interop [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr LoadLibrary(string fileName); + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi)] + public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName); + [DllImport("comdlg32.dll", CharSet = CharSet.Unicode, EntryPoint = "GetSaveFileNameW")] public static extern bool GetSaveFileName(IntPtr lpofn); @@ -970,6 +978,9 @@ namespace Avalonia.Win32.Interop [DllImport("shcore.dll")] public static extern void SetProcessDpiAwareness(PROCESS_DPI_AWARENESS value); + [DllImport("user32.dll", SetLastError = true)] + public static extern bool SetProcessDpiAwarenessContext(IntPtr dpiAWarenessContext); + [DllImport("shcore.dll")] public static extern long GetDpiForMonitor(IntPtr hmonitor, MONITOR_DPI_TYPE dpiType, out uint dpiX, out uint dpiY); @@ -1078,7 +1089,6 @@ namespace Avalonia.Win32.Interop } } - public enum PROCESS_DPI_AWARENESS { PROCESS_DPI_UNAWARE = 0, diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index 33bee4a82a..47a91983c9 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -19,6 +19,7 @@ using Avalonia.Rendering; using Avalonia.Threading; using Avalonia.Win32.Input; using Avalonia.Win32.Interop; +using static Avalonia.Win32.Interop.UnmanagedMethods; namespace Avalonia { @@ -48,17 +49,7 @@ namespace Avalonia.Win32 public Win32Platform() { - // Declare that this process is aware of per monitor DPI - if (UnmanagedMethods.ShCoreAvailable) - { - var osVersion = Environment.OSVersion.Version; - if (osVersion.Major > 6 || (osVersion.Major == 6 && osVersion.Minor > 2)) - { - UnmanagedMethods.SetProcessDpiAwareness(UnmanagedMethods.PROCESS_DPI_AWARENESS - .PROCESS_PER_MONITOR_DPI_AWARE); - } - } - + SetDpiAwareness(); CreateMessageWindow(); } @@ -77,8 +68,6 @@ namespace Avalonia.Win32 public static void Initialize(bool deferredRendering = true) { - UnmanagedMethods.SetProcessDPIAware(); - AvaloniaLocator.CurrentMutable .Bind().ToSingleton() .Bind().ToConstant(CursorFactory.Instance) @@ -250,5 +239,33 @@ namespace Avalonia.Win32 } } + private static void SetDpiAwareness() + { + // Ideally we'd set DPI awareness in the manifest but this doesn't work for netcoreapp2.0 + // apps as they are actually dlls run by a console loader. Instead we have to do it in code, + // but there are various ways to do this depending on the OS version. + var user32 = LoadLibrary("user32.dll"); + var method = GetProcAddress(user32, nameof(SetProcessDpiAwarenessContext)); + + if (method != null) + { + if (SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) || + SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE)) + { + return; + } + } + + var shcore = LoadLibrary("shcore.dll"); + method = GetProcAddress(shcore, nameof(SetProcessDpiAwareness)); + + if (method != null) + { + SetProcessDpiAwareness(PROCESS_DPI_AWARENESS.PROCESS_PER_MONITOR_DPI_AWARE); + return; + } + + SetProcessDPIAware(); + } } }