Browse Source

Merge pull request #2010 from AvaloniaUI/fixes/2007-win32-dpi-scaling

Fix DPI scaling on win32
Jumar Macato 7 years ago
parent
commit
6cc82b41cb

+ 11 - 1
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,

+ 30 - 13
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<IClipboard>().ToSingleton<ClipboardImpl>()
                 .Bind<IStandardCursorFactory>().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();
+        }
     }
 }

+ 7 - 1
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<UnmanagedMethods.RECT>(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: