Skip to content

Commit

Permalink
feat: DpiAware
Browse files Browse the repository at this point in the history
  • Loading branch information
emako committed Sep 24, 2024
1 parent 426a0f4 commit cbe2f67
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 40 deletions.
27 changes: 27 additions & 0 deletions src/Wpf.Ui.Violeta/Win32/DpiAware.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;
using System.Windows.Media;

namespace Wpf.Ui.Violeta.Win32;

public static class DpiAware
{
/// <summary>
/// <see cref="DisableDpiAwarenessAttribute"/>
/// </summary>
public static bool SetProcessDpiAwareness()
{
if (NTdll.RtlGetVersion(out NTdll.OSVERSIONINFOEX osv) == NTdll.NTStatus.STATUS_SUCCESS)
{
Version osVersion = new(osv.MajorVersion, osv.MinorVersion, osv.BuildNumber, osv.PlatformId);

if (Environment.OSVersion.Platform == PlatformID.Win32NT && osVersion >= new Version(6, 3))
{
if (SHCore.SetProcessDpiAwareness(SHCore.PROCESS_DPI_AWARENESS.PROCESS_PER_MONITOR_DPI_AWARE) == 0)
{
return true;
}
}
}
return false;
}
}
32 changes: 5 additions & 27 deletions src/Wpf.Ui.Violeta/Win32/DpiHelper.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
using System.Runtime.InteropServices;
using System.Security;

namespace Wpf.Ui.Violeta.Win32;
namespace Wpf.Ui.Violeta.Win32;

internal static class DpiHelper
{
Expand All @@ -10,29 +7,10 @@ internal static class DpiHelper

private static (float X, float Y) GetScale()
{
nint hdc = GetDC(0);
float scaleX = GetDeviceCaps(hdc, DeviceCap.LOGPIXELSX);
float scaleY = GetDeviceCaps(hdc, DeviceCap.LOGPIXELSY);
_ = ReleaseDC(0, hdc);
nint hdc = User32.GetDC(0);
float scaleX = User32.GetDeviceCaps(hdc, User32.DeviceCap.LOGPIXELSX);
float scaleY = User32.GetDeviceCaps(hdc, User32.DeviceCap.LOGPIXELSY);
_ = User32.ReleaseDC(0, hdc);
return new(scaleX / 96f, scaleY / 96f);
}

[SecurityCritical]
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
private static extern nint GetDC(nint hWnd);

[SecurityCritical]
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
private static extern int ReleaseDC(nint hWnd, nint hDC);

[DllImport("gdi32.dll", SetLastError = false, ExactSpelling = true)]
private static extern int GetDeviceCaps(nint hdc, DeviceCap nIndex);

private enum DeviceCap
{
LOGPIXELSX = 88,
LOGPIXELSY = 90,
}
}
30 changes: 17 additions & 13 deletions src/Wpf.Ui.Violeta/Win32/NTdll.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,28 @@ namespace Wpf.Ui.Violeta.Win32;

internal static class NTdll
{
[SecurityCritical]
[DllImport("ntdll.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
public static extern int RtlGetVersion(out OSVERSIONINFOEX versionInfo);

public static Version GetOSVersion()
{
if (RtlGetVersion(out OSVERSIONINFOEX osv) == 0)
{
return new Version(osv.MajorVersion, osv.MinorVersion, osv.BuildNumber, osv.Revision);

Check failure on line 18 in src/Wpf.Ui.Violeta/Win32/NTdll.cs

View workflow job for this annotation

GitHub Actions / build

'NTdll.OSVERSIONINFOEX' does not contain a definition for 'Revision' and no accessible extension method 'Revision' accepting a first argument of type 'NTdll.OSVERSIONINFOEX' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 18 in src/Wpf.Ui.Violeta/Win32/NTdll.cs

View workflow job for this annotation

GitHub Actions / build

'NTdll.OSVERSIONINFOEX' does not contain a definition for 'Revision' and no accessible extension method 'Revision' accepting a first argument of type 'NTdll.OSVERSIONINFOEX' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 18 in src/Wpf.Ui.Violeta/Win32/NTdll.cs

View workflow job for this annotation

GitHub Actions / build

'NTdll.OSVERSIONINFOEX' does not contain a definition for 'Revision' and no accessible extension method 'Revision' accepting a first argument of type 'NTdll.OSVERSIONINFOEX' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 18 in src/Wpf.Ui.Violeta/Win32/NTdll.cs

View workflow job for this annotation

GitHub Actions / build

'NTdll.OSVERSIONINFOEX' does not contain a definition for 'Revision' and no accessible extension method 'Revision' accepting a first argument of type 'NTdll.OSVERSIONINFOEX' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 18 in src/Wpf.Ui.Violeta/Win32/NTdll.cs

View workflow job for this annotation

GitHub Actions / build

'NTdll.OSVERSIONINFOEX' does not contain a definition for 'Revision' and no accessible extension method 'Revision' accepting a first argument of type 'NTdll.OSVERSIONINFOEX' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 18 in src/Wpf.Ui.Violeta/Win32/NTdll.cs

View workflow job for this annotation

GitHub Actions / build

'NTdll.OSVERSIONINFOEX' does not contain a definition for 'Revision' and no accessible extension method 'Revision' accepting a first argument of type 'NTdll.OSVERSIONINFOEX' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 18 in src/Wpf.Ui.Violeta/Win32/NTdll.cs

View workflow job for this annotation

GitHub Actions / build

'NTdll.OSVERSIONINFOEX' does not contain a definition for 'Revision' and no accessible extension method 'Revision' accepting a first argument of type 'NTdll.OSVERSIONINFOEX' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 18 in src/Wpf.Ui.Violeta/Win32/NTdll.cs

View workflow job for this annotation

GitHub Actions / build

'NTdll.OSVERSIONINFOEX' does not contain a definition for 'Revision' and no accessible extension method 'Revision' accepting a first argument of type 'NTdll.OSVERSIONINFOEX' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 18 in src/Wpf.Ui.Violeta/Win32/NTdll.cs

View workflow job for this annotation

GitHub Actions / build

'NTdll.OSVERSIONINFOEX' does not contain a definition for 'Revision' and no accessible extension method 'Revision' accepting a first argument of type 'NTdll.OSVERSIONINFOEX' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 18 in src/Wpf.Ui.Violeta/Win32/NTdll.cs

View workflow job for this annotation

GitHub Actions / build

'NTdll.OSVERSIONINFOEX' does not contain a definition for 'Revision' and no accessible extension method 'Revision' accepting a first argument of type 'NTdll.OSVERSIONINFOEX' could be found (are you missing a using directive or an assembly reference?)
}

return Environment.OSVersion.Version;
}

[StructLayout(LayoutKind.Sequential)]
public struct OSVERSIONINFOEX
{
public int OSVersionInfoSize;
public int MajorVersion;
public int MinorVersion;
public int BuildNumber;
public int Revision;
public int PlatformId;

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
Expand All @@ -26,18 +40,8 @@ public struct OSVERSIONINFOEX
public byte Reserved;
}

[SecurityCritical]
[DllImport("ntdll.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
public static extern int RtlGetVersion(out OSVERSIONINFOEX versionInfo);

public static Version GetOSVersion()
public static class NTStatus
{
if (RtlGetVersion(out OSVERSIONINFOEX osv) == 0)
{
return new Version(osv.MajorVersion, osv.MinorVersion, osv.BuildNumber, osv.Revision);
}

return Environment.OSVersion.Version;
public const int STATUS_SUCCESS = 0;
}
}
27 changes: 27 additions & 0 deletions src/Wpf.Ui.Violeta/Win32/SHCore.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Runtime.InteropServices;

namespace Wpf.Ui.Violeta.Win32;

internal static class SHCore
{
[DllImport("shcore.dll")]
public static extern uint SetProcessDpiAwareness(PROCESS_DPI_AWARENESS awareness);

[DllImport("shcore.dll")]
public static extern int GetDpiForMonitor(nint hmonitor, MONITOR_DPI_TYPE dpiType, out uint dpiX, out uint dpiY);

public enum PROCESS_DPI_AWARENESS
{
PROCESS_DPI_UNAWARE,
PROCESS_SYSTEM_DPI_AWARE,
PROCESS_PER_MONITOR_DPI_AWARE,
}

public enum MONITOR_DPI_TYPE
{
MDT_EFFECTIVE_DPI = 0,
MDT_ANGULAR_DPI,
MDT_RAW_DPI,
MDT_DEFAULT = MDT_EFFECTIVE_DPI,
}
}
20 changes: 20 additions & 0 deletions src/Wpf.Ui.Violeta/Win32/User32.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
using System;
using System.Runtime.InteropServices;
using System.Security;

namespace Wpf.Ui.Violeta.Win32;

internal static class User32
{
[SecurityCritical]
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
public static extern nint GetDC(nint hWnd);

[SecurityCritical]
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
public static extern int ReleaseDC(nint hWnd, nint hDC);

[DllImport("gdi32.dll", SetLastError = false, ExactSpelling = true)]
public static extern int GetDeviceCaps(nint hdc, DeviceCap nIndex);

[DllImport("user32.dll", SetLastError = false, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetForegroundWindow(nint hWnd);
Expand All @@ -31,4 +45,10 @@ public enum DialogBoxCommand : uint
IDTRYAGAIN = 9,
IDCONTINUE = 10,
}

public enum DeviceCap
{
LOGPIXELSX = 88,
LOGPIXELSY = 90,
}
}

0 comments on commit cbe2f67

Please sign in to comment.