UEZ Posted 2 hours ago Posted 2 hours ago I think the _WinAPI_DPI UDF is now complete enough to be released here. expandcollapse popup;Coded by UEZ build 2026-08-12 beta #include-once #include <GDIPlus.au3> #include <StructureConstants.au3> #include <WinAPIGdi.au3> #include <WinAPISysWin.au3> #include <WinAPIsysinfoConstants.au3> #Region DPI Constants ;https://learn.microsoft.com/en-us/windows/win32/api/windef/ne-windef-dpi_awareness Global Enum $DPI_AWARENESS_INVALID = -1, $DPI_AWARENESS_UNAWARE = 0, $DPI_AWARENESS_SYSTEM_AWARE = 1, $DPI_AWARENESS_PER_MONITOR_AWARE = 2 ;https://learn.microsoft.com/en-us/windows/win32/hidpi/dpi-awareness-context ;These are pseudo-handles: formally pointers (DECLARE_HANDLE), but the values -1..-5 are ;sentinels the API maps internally. Never dereference them. Kept as plain literals on ;purpose - deriving them from the DPI_AWARENESS enum couples two unrelated enumerations. Global Const $DPI_AWARENESS_CONTEXT_UNAWARE = -1 Global Const $DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = -2 Global Const $DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = -3 Global Const $DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = -4 Global Const $DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED = -5 ;Unified awareness levels for _WinAPI_SetDPIAwareness (version independent). ;Note the ordering is NOT "increasing awareness": GDISCALED is a variant of UNAWARE and ;deliberately sits last, so never clamp an out-of-range value to it. Global Enum $DPI_LEVEL_UNAWARE = 0, $DPI_LEVEL_SYSTEM, $DPI_LEVEL_PER_MONITOR, $DPI_LEVEL_PER_MONITOR_V2, $DPI_LEVEL_UNAWARE_GDISCALED ;Scope for _WinAPI_SetDPIAwareness Global Enum $DPI_SCOPE_PROCESS = 1, $DPI_SCOPE_THREAD = 2 ;enum PROCESS_DPI_AWARENESS (shellscalingapi.h) Global Enum $PROCESS_DPI_UNAWARE = 0, $PROCESS_SYSTEM_DPI_AWARE, $PROCESS_PER_MONITOR_DPI_AWARE ;enum _MONITOR_DPI_TYPE Global Enum $MDT_EFFECTIVE_DPI = 0, $MDT_ANGULAR_DPI, $MDT_RAW_DPI Global Const $MDT_DEFAULT = $MDT_EFFECTIVE_DPI ;Windows Message Codes Global Const $WM_DPICHANGED = 0x02E0, $WM_DPICHANGED_BEFOREPARENT = 0x02E2, $WM_DPICHANGED_AFTERPARENT = 0x02E3, $WM_GETDPISCALEDSIZE = 0x02E4 ;DpiChangeBehavior Global Const $DDC_DEFAULT = 0 Global Const $DDC_DISABLE_ALL = 1 Global Const $DDC_DISABLE_RESIZE = 2 Global Const $DDC_DISABLE_CONTROL_RELAYOUT = 4 Global Const $DCDC_DEFAULT = 0 Global Const $DCDC_DISABLE_FONT_UPDATE = 1 Global Const $DCDC_DISABLE_RELAYOUT = 2 ;Internal: OS build thresholds. @OSBuild is compared against these instead of using ;hand-written Case ranges, which is where the original version left a gap. Global Const $__DPI_BUILD_VISTA = 6000 ;SetProcessDPIAware Global Const $__DPI_BUILD_WIN81 = 9600 ;SetProcessDpiAwareness, GetDpiForMonitor Global Const $__DPI_BUILD_1607 = 14393 ;SetThreadDpiAwarenessContext, GetDpiForSystem, ... Global Const $__DPI_BUILD_1703 = 15063 ;SetProcessDpiAwarenessContext, PER_MONITOR_AWARE_V2 Global Const $__DPI_BUILD_1803 = 17134 ;GetDpiFromDpiAwarenessContext, InheritWindowMonitor Global Const $__DPI_BUILD_1809 = 17763 ;UNAWARE_GDISCALED Global Const $__DPI_ERROR_ACCESS_DENIED = 5 ;Win32 ERROR_ACCESS_DENIED Global Const $__DPI_E_ACCESSDENIED = 0x80070005 ;HRESULT E_ACCESSDENIED Global Const $__DPI_DEFAULT_DPI = 96 ;USER_DEFAULT_SCREEN_DPI #EndRegion DPI Constants #Region Internal helpers ;Reads the thread's last Win32 error. Must be called immediately after the DllCall whose ;failure reason is wanted - any intervening call may overwrite it. Func __WinAPI_DPI_GetLastError() Local $aResult = DllCall("kernel32.dll", "dword", "GetLastError") If @error Or Not IsArray($aResult) Then Return 0 Return $aResult[0] EndFunc ;==>__WinAPI_DPI_GetLastError ;Best available way of reading the current DPI, degrading with the OS version. ;Returns 0 if nothing worked. Func __WinAPI_DPI_QueryCurrent() Local $iDPI = 0 If @OSBuild >= $__DPI_BUILD_1607 Then $iDPI = _WinAPI_GetDpiForSystem() If @error Then $iDPI = 0 EndIf If Not $iDPI And @OSBuild >= $__DPI_BUILD_WIN81 Then $iDPI = _WinAPI_GetDpiForMonitor() If @error Then $iDPI = 0 EndIf If Not $iDPI Then $iDPI = _WinAPI_GetDPI() ;GetDeviceCaps fallback, works on every version If @error Then $iDPI = 0 EndIf Return $iDPI EndFunc ;==>__WinAPI_DPI_QueryCurrent ;Maps a unified level (0..4) to its DPI_AWARENESS_CONTEXT pseudo-handle. Func __WinAPI_DPI_LevelToContext($iLevel) Switch $iLevel Case $DPI_LEVEL_UNAWARE Return $DPI_AWARENESS_CONTEXT_UNAWARE Case $DPI_LEVEL_SYSTEM Return $DPI_AWARENESS_CONTEXT_SYSTEM_AWARE Case $DPI_LEVEL_PER_MONITOR Return $DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE Case $DPI_LEVEL_PER_MONITOR_V2 Return $DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 Case Else Return $DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED EndSwitch EndFunc ;==>__WinAPI_DPI_LevelToContext #EndRegion Internal helpers #Region WinAPI DPI - queries ;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-adjustwindowrectexfordpi ;The API expands an existing client RECT into a window RECT, so the rectangle is an input. ;Passing all four coordinates as 0 yields the pure frame offsets (left/top become negative). ;Returns: $tagRECT structure. @error 1 = call failed (@extended = @error), 2 = API returned FALSE. Func _WinAPI_AdjustWindowRectExForDpi($iDpi, $dwStyle, $dwExStyle = 0, $bMenu = False, $iLeft = 0, $iTop = 0, $iRight = 0, $iBottom = 0) Local $tRECT = DllStructCreate($tagRECT) $tRECT.Left = $iLeft $tRECT.Top = $iTop $tRECT.Right = $iRight $tRECT.Bottom = $iBottom ;Parameter order per MSDN: lpRect, dwStyle, bMenu, dwExStyle, dpi Local $aResult = DllCall("user32.dll", "bool", "AdjustWindowRectExForDpi", "struct*", $tRECT, "dword", $dwStyle, "bool", $bMenu, "dword", $dwExStyle, "uint", $iDpi) ;Win10 1607+ / no server support If @error Or Not IsArray($aResult) Then Return SetError(1, @error, 0) If Not $aResult[0] Then Return SetError(2, __WinAPI_DPI_GetLastError(), 0) Return $tRECT EndFunc ;==>_WinAPI_AdjustWindowRectExForDpi ;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfofordpi ;$pvParam must be a DllStruct (or pointer) matching the requested $uiAction. Func _WinAPI_SystemParametersInfoForDpi($uiAction, $uiParam, $pvParam, $fWinIni, $iDpi) Local $aResult = DllCall("user32.dll", "bool", "SystemParametersInfoForDpi", "uint", $uiAction, "uint", $uiParam, "struct*", $pvParam, "uint", $fWinIni, "uint", $iDpi) ;Win10 1607+ / no server support If @error Or Not IsArray($aResult) Then Return SetError(1, @error, 0) If Not $aResult[0] Then Return SetError(2, __WinAPI_DPI_GetLastError(), 0) Return True EndFunc ;==>_WinAPI_SystemParametersInfoForDpi ;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-inheritwindowmonitor Func _WinAPI_InheritWindowMonitor($hWnd, $hWndInherit) Local $aResult = DllCall("user32.dll", "bool", "InheritWindowMonitor", "hwnd", $hWnd, "hwnd", $hWndInherit) ;Win10 1803+ / Windows Server 2016+ If @error Or Not IsArray($aResult) Then Return SetError(1, @error, 0) If Not $aResult[0] Then Return SetError(2, __WinAPI_DPI_GetLastError(), 0) Return True EndFunc ;==>_WinAPI_InheritWindowMonitor ;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-isvaliddpiawarenesscontext ;A result of False means "this context is not valid" - that is an answer, not an error. ;@error is only set when the call itself could not be made. Func _WinAPI_IsValidDpiAwarenessContext($iContext) Local $aResult = DllCall("user32.dll", "bool", "IsValidDpiAwarenessContext", "int_ptr", $iContext) ;Win10 1607+ / no server support If @error Or Not IsArray($aResult) Then Return SetError(1, @error, False) Return ($aResult[0] <> 0) EndFunc ;==>_WinAPI_IsValidDpiAwarenessContext ;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-logicaltophysicalpointforpermonitordpi Func _WinAPI_LogicalToPhysicalPointForPerMonitorDPI($hWnd, $iX, $iY) Local $tPOINT = DllStructCreate($tagPOINT) $tPOINT.x = $iX $tPOINT.y = $iY Local $aResult = DllCall("user32.dll", "bool", "LogicalToPhysicalPointForPerMonitorDPI", "hwnd", $hWnd, "struct*", $tPOINT) ;Win8.1+ / no server support If @error Or Not IsArray($aResult) Then Return SetError(1, @error, 0) If Not $aResult[0] Then Return SetError(2, __WinAPI_DPI_GetLastError(), 0) Return $tPOINT EndFunc ;==>_WinAPI_LogicalToPhysicalPointForPerMonitorDPI ;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-physicaltologicalpointforpermonitordpi Func _WinAPI_PhysicalToLogicalPointForPerMonitorDPI($hWnd, $iX, $iY) Local $tPOINT = DllStructCreate($tagPOINT) $tPOINT.x = $iX $tPOINT.y = $iY Local $aResult = DllCall("user32.dll", "bool", "PhysicalToLogicalPointForPerMonitorDPI", "hwnd", $hWnd, "struct*", $tPOINT) ;Win8.1+ / no server support If @error Or Not IsArray($aResult) Then Return SetError(1, @error, 0) If Not $aResult[0] Then Return SetError(2, __WinAPI_DPI_GetLastError(), 0) Return $tPOINT EndFunc ;==>_WinAPI_PhysicalToLogicalPointForPerMonitorDPI ;GDI+ based DPI of a window (0 = desktop). Requires _GDIPlus_Startup beforehand. Func _GDIPlus_GetDPI($hGUI = 0) Local $hGfx = _GDIPlus_GraphicsCreateFromHWND($hGUI) If @error Then Return SetError(1, @error, 0) Local $aResult = DllCall($__g_hGDIPDll, "int", "GdipGetDpiX", "handle", $hGfx, "float*", 0) Local $iErr = @error _GDIPlus_GraphicsDispose($hGfx) ;always dispose, even on failure - the original leaked here If $iErr Or Not IsArray($aResult) Then Return SetError(2, $iErr, 0) If $aResult[0] Then Return SetError(3, $aResult[0], 0) ;GDI+ status, 0 = Ok Return $aResult[2] EndFunc ;==>_GDIPlus_GetDPI ;GetDeviceCaps based DPI. Works on every Windows version, but returns a virtualised value ;when the calling thread is DPI unaware. Func _WinAPI_GetDPI($hWnd = 0) If Not $hWnd Then $hWnd = _WinAPI_GetDesktopWindow() Local Const $hDC = _WinAPI_GetDC($hWnd) If @error Or Not $hDC Then Return SetError(1, 0, 0) Local Const $iDPI = _WinAPI_GetDeviceCaps($hDC, $LOGPIXELSX) Local Const $iErr = @error _WinAPI_ReleaseDC($hWnd, $hDC) If $iErr Or Not $iDPI Then Return SetError(2, $iErr, 0) Return $iDPI EndFunc ;==>_WinAPI_GetDPI ;https://learn.microsoft.com/en-us/windows/win32/api/shellscalingapi/nf-shellscalingapi-getdpiformonitor ;$hMonitor = 0 auto-selects the primary monitor. ;$bBothAxes = True returns a 2-element array [dpiX, dpiY] instead of dpiX only. Func _WinAPI_GetDpiForMonitor($hMonitor = 0, $iDpiType = $MDT_DEFAULT, $bBothAxes = False) If Not $hMonitor Then Local $aMonitors = _WinAPI_EnumDisplayMonitors() If @error Or Not IsArray($aMonitors) Then Return SetError(1, @error, 0) Local $aMI For $i = 1 To $aMonitors[0][0] $aMI = _WinAPI_GetMonitorInfo($aMonitors[$i][0]) If @error Or Not IsArray($aMI) Then ContinueLoop ;$aMI[2] is a flags field - test the bit, do not compare for equality If BitAND($aMI[2], 1) Then ;MONITORINFOF_PRIMARY $hMonitor = $aMonitors[$i][0] ExitLoop EndIf Next If Not $hMonitor Then Return SetError(2, 0, 0) ;no primary monitor found EndIf Local $tDpiX = DllStructCreate("uint dpiX") Local $tDpiY = DllStructCreate("uint dpiY") Local $aResult = DllCall("Shcore.dll", "long", "GetDpiForMonitor", _ ;Win8.1+ "handle", $hMonitor, _ "long", $iDpiType, _ "struct*", $tDpiX, _ "struct*", $tDpiY) If @error Or Not IsArray($aResult) Then Return SetError(3, @error, 0) If $aResult[0] <> 0 Then Return SetError(4, $aResult[0], 0) ;HRESULT check If Not $bBothAxes Then Return $tDpiX.dpiX Local $aReturn[2] = [$tDpiX.dpiX, $tDpiY.dpiY] Return $aReturn EndFunc ;==>_WinAPI_GetDpiForMonitor ;Kept for source compatibility - returns [dpiX, dpiY]. Func _WinAPI_GetDpiForMonitor2($hMonitor, $iDpiType = $MDT_EFFECTIVE_DPI) Local $aReturn = _WinAPI_GetDpiForMonitor($hMonitor, $iDpiType, True) Return SetError(@error, @extended, $aReturn) EndFunc ;==>_WinAPI_GetDpiForMonitor2 ;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdpiforwindow Func _WinAPI_GetDpiForWindow($hWnd) Local $aResult = DllCall("user32.dll", "uint", "GetDpiForWindow", "hwnd", $hWnd) ;Win10 1607+ / no server support If @error Or Not IsArray($aResult) Then Return SetError(1, @error, 0) If Not $aResult[0] Then Return SetError(2, 0, 0) ;0 = invalid hwnd Return $aResult[0] EndFunc ;==>_WinAPI_GetDpiForWindow ;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdpiforsystem Func _WinAPI_GetDpiForSystem() Local $aResult = DllCall("user32.dll", "uint", "GetDpiForSystem") ;Win10 1607+ / no server support If @error Or Not IsArray($aResult) Then Return SetError(1, @error, 0) If Not $aResult[0] Then Return SetError(2, 0, 0) Return $aResult[0] EndFunc ;==>_WinAPI_GetDpiForSystem ;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getthreaddpiawarenesscontext ;Returns a REAL context handle, never one of the -1..-5 sentinels. Do not compare it with ;the DPI_AWARENESS_CONTEXT_* constants directly - use _WinAPI_AreDpiAwarenessContextsEqual. Func _WinAPI_GetThreadDpiAwarenessContext() Local $aResult = DllCall("user32.dll", "int_ptr", "GetThreadDpiAwarenessContext") ;Win10 1607+ / no server support If @error Or Not IsArray($aResult) Then Return SetError(1, @error, 0) If Not $aResult[0] Then Return SetError(2, 0, 0) Return $aResult[0] EndFunc ;==>_WinAPI_GetThreadDpiAwarenessContext ;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdpifromdpiawarenesscontext ;MSDN: PER_MONITOR_AWARE and PER_MONITOR_AWARE_V2 contexts return 0 because the real DPI ;cannot be determined without an HWND. 0 is therefore NOT an error here. Func _WinAPI_GetDpiFromDpiAwarenessContext($iContext) Local $aResult = DllCall("user32.dll", "uint", "GetDpiFromDpiAwarenessContext", "int_ptr", $iContext) ;Win10 1803+ / Windows Server 2016+ If @error Or Not IsArray($aResult) Then Return SetError(1, @error, 0) Return $aResult[0] EndFunc ;==>_WinAPI_GetDpiFromDpiAwarenessContext ;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getawarenessfromdpiawarenesscontext ;Returns a DPI_AWARENESS value. 0 = DPI_AWARENESS_UNAWARE is a valid result, and the "int" ;return type keeps DPI_AWARENESS_INVALID (-1) intact instead of turning it into 4294967295. ;Note: PER_MONITOR_AWARE and PER_MONITOR_AWARE_V2 both report 2 - use ;_WinAPI_AreDpiAwarenessContextsEqual to distinguish them. Func _WinAPI_GetAwarenessFromDpiAwarenessContext($iContext) Local $aResult = DllCall("user32.dll", "int", "GetAwarenessFromDpiAwarenessContext", "int_ptr", $iContext) ;Win10 1607+ / no server support If @error Or Not IsArray($aResult) Then Return SetError(1, @error, $DPI_AWARENESS_INVALID) Return $aResult[0] EndFunc ;==>_WinAPI_GetAwarenessFromDpiAwarenessContext ;Convenience: DPI_AWARENESS of the calling thread. Func _WinAPI_GetThreadDpiAwareness() Local $iContext = _WinAPI_GetThreadDpiAwarenessContext() If @error Then Return SetError(1, @error, $DPI_AWARENESS_INVALID) Local $iAwareness = _WinAPI_GetAwarenessFromDpiAwarenessContext($iContext) If @error Then Return SetError(2, @error, $DPI_AWARENESS_INVALID) Return $iAwareness EndFunc ;==>_WinAPI_GetThreadDpiAwareness ;Convenience: does the calling thread run in the given context? Func _WinAPI_IsThreadDpiAwarenessContext($iContext) Local $iCurrent = _WinAPI_GetThreadDpiAwarenessContext() If @error Then Return SetError(1, @error, False) Local $bEqual = _WinAPI_AreDpiAwarenessContextsEqual($iCurrent, $iContext) If @error Then Return SetError(2, @error, False) Return $bEqual EndFunc ;==>_WinAPI_IsThreadDpiAwarenessContext ;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdpiawarenesscontextforprocess Func _WinAPI_GetDpiAwarenessContextForProcess($hProcess = 0) Local $aResult = DllCall("user32.dll", "int_ptr", "GetDpiAwarenessContextForProcess", "handle", $hProcess) ;Win10 1803+ / Windows Server 2016+ If @error Or Not IsArray($aResult) Then Return SetError(1, @error, 0) If Not $aResult[0] Then Return SetError(2, 0, 0) Return $aResult[0] EndFunc ;==>_WinAPI_GetDpiAwarenessContextForProcess ;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsystemdpiforprocess Func _WinAPI_GetSystemDpiForProcess($hProcess = 0) Local $aResult = DllCall("user32.dll", "uint", "GetSystemDpiForProcess", "handle", $hProcess) ;Win10 1803+ / Windows Server 2016+ If @error Or Not IsArray($aResult) Then Return SetError(1, @error, 0) If Not $aResult[0] Then Return SetError(2, 0, 0) Return $aResult[0] EndFunc ;==>_WinAPI_GetSystemDpiForProcess ;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowdpiawarenesscontext Func _WinAPI_GetWindowDpiAwarenessContext($hWnd) Local $aResult = DllCall("user32.dll", "int_ptr", "GetWindowDpiAwarenessContext", "hwnd", $hWnd) ;Win10 1607+ / Windows Server 2016+ If @error Or Not IsArray($aResult) Then Return SetError(1, @error, 0) If Not $aResult[0] Then Return SetError(2, 0, 0) ;NULL = invalid window Return $aResult[0] EndFunc ;==>_WinAPI_GetWindowDpiAwarenessContext ;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-aredpiawarenesscontextsequal ;False is a valid answer, so @error is only set when the call itself failed. Func _WinAPI_AreDpiAwarenessContextsEqual($iContextA, $iContextB) Local $aResult = DllCall("user32.dll", "bool", "AreDpiAwarenessContextsEqual", _ ;Win10 1607+ "int_ptr", $iContextA, _ "int_ptr", $iContextB) If @error Or Not IsArray($aResult) Then Return SetError(1, @error, False) Return ($aResult[0] <> 0) EndFunc ;==>_WinAPI_AreDpiAwarenessContextsEqual ;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsystemmetricsfordpi ;0 can be a legitimate metric value, so it is not treated as an error. Func _WinAPI_GetSystemMetricsForDpi($nIndex, $iDpi) Local $aResult = DllCall("user32.dll", "int", "GetSystemMetricsForDpi", "int", $nIndex, "uint", $iDpi) ;Win10 1607+ If @error Or Not IsArray($aResult) Then Return SetError(1, @error, 0) Return $aResult[0] EndFunc ;==>_WinAPI_GetSystemMetricsForDpi ;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdialogdpichangebehavior ;Returns a DDC_* bit mask. $DDC_DEFAULT (0) is a valid result. Func _WinAPI_GetDialogDpiChangeBehavior($hWnd) Local $aResult = DllCall("user32.dll", "int", "GetDialogDpiChangeBehavior", "hwnd", $hWnd) ;Win10 1703+ If @error Or Not IsArray($aResult) Then Return SetError(1, @error, -1) Return $aResult[0] EndFunc ;==>_WinAPI_GetDialogDpiChangeBehavior ;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdialogcontroldpichangebehavior ;Returns a DCDC_* bit mask. $DCDC_DEFAULT (0) is a valid result. Func _WinAPI_GetDialogControlDpiChangeBehavior($hWnd) Local $aResult = DllCall("user32.dll", "int", "GetDialogControlDpiChangeBehavior", "hwnd", $hWnd) ;Win10 1703+ If @error Or Not IsArray($aResult) Then Return SetError(1, @error, -1) Return $aResult[0] EndFunc ;==>_WinAPI_GetDialogControlDpiChangeBehavior #EndRegion WinAPI DPI - queries #Region WinAPI DPI - setters ;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setprocessdpiawarenesscontext ;@extended carries GetLastError() on failure - ERROR_ACCESS_DENIED (5) means the awareness ;was already fixed (typically by the application manifest) and cannot be changed. Func _WinAPI_SetProcessDpiAwarenessContext($iContext) Local $aResult = DllCall("user32.dll", "bool", "SetProcessDpiAwarenessContext", "int_ptr", $iContext) ;Win10 1703+ / Windows Server 2016+ If @error Or Not IsArray($aResult) Then Return SetError(1, @error, False) If Not $aResult[0] Then Return SetError(2, __WinAPI_DPI_GetLastError(), False) Return True EndFunc ;==>_WinAPI_SetProcessDpiAwarenessContext ;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setthreaddpiawarenesscontext ;Returns the PREVIOUS context - keep it and restore it when you are done, otherwise you ;change the behaviour of unrelated code running on the same thread. Func _WinAPI_SetThreadDpiAwarenessContext($iContext) Local $aResult = DllCall("user32.dll", "int_ptr", "SetThreadDpiAwarenessContext", "int_ptr", $iContext) ;Win10 1607+ / Windows Server 2016+ If @error Or Not IsArray($aResult) Then Return SetError(1, @error, 0) If Not $aResult[0] Then Return SetError(2, __WinAPI_DPI_GetLastError(), 0) ;NULL = invalid context Return $aResult[0] EndFunc ;==>_WinAPI_SetThreadDpiAwarenessContext ;https://learn.microsoft.com/en-us/windows/win32/api/shellscalingapi/nf-shellscalingapi-setprocessdpiawareness ;$iAwareness is a PROCESS_DPI_AWARENESS value (0..2), not a DPI_AWARENESS_CONTEXT. ;@extended carries the HRESULT - E_ACCESSDENIED (0x80070005) means "already set". Func _WinAPI_SetProcessDpiAwareness($iAwareness = $PROCESS_PER_MONITOR_DPI_AWARE) Local $aResult = DllCall("Shcore.dll", "long", "SetProcessDpiAwareness", "int", $iAwareness) ;Win8.1+ / Server 2012 R2+ If @error Or Not IsArray($aResult) Then Return SetError(1, @error, False) If $aResult[0] <> 0 Then Return SetError(2, $aResult[0], False) Return True EndFunc ;==>_WinAPI_SetProcessDpiAwareness ;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setprocessdpiaware Func _WinAPI_SetProcessDPIAware() Local $aResult = DllCall("user32.dll", "bool", "SetProcessDPIAware") ;Vista+ If @error Or Not IsArray($aResult) Then Return SetError(1, @error, False) If Not $aResult[0] Then Return SetError(2, __WinAPI_DPI_GetLastError(), False) Return True EndFunc ;==>_WinAPI_SetProcessDPIAware ;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enablenonclientdpiscaling ;Unnecessary in PER_MONITOR_AWARE_V2 contexts - V2 scales the non-client area already. Func _WinAPI_EnableNonClientDpiScaling($hWnd) Local $aResult = DllCall("user32.dll", "bool", "EnableNonClientDpiScaling", "hwnd", $hWnd) ;Win10 1607+ If @error Or Not IsArray($aResult) Then Return SetError(1, @error, False) If Not $aResult[0] Then Return SetError(2, __WinAPI_DPI_GetLastError(), False) Return True EndFunc ;==>_WinAPI_EnableNonClientDpiScaling ;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setdialogdpichangebehavior Func _WinAPI_SetDialogDpiChangeBehavior($hWnd, $iMask, $iValues) Local $aResult = DllCall("user32.dll", "bool", "SetDialogDpiChangeBehavior", "hwnd", $hWnd, "int", $iMask, "int", $iValues) ;Win10 1703+ If @error Or Not IsArray($aResult) Then Return SetError(1, @error, False) If Not $aResult[0] Then Return SetError(2, __WinAPI_DPI_GetLastError(), False) Return True EndFunc ;==>_WinAPI_SetDialogDpiChangeBehavior ;https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setdialogcontroldpichangebehavior Func _WinAPI_SetDialogControlDpiChangeBehavior($hWnd, $iMask, $iValues) Local $aResult = DllCall("user32.dll", "bool", "SetDialogControlDpiChangeBehavior", "hwnd", $hWnd, "int", $iMask, "int", $iValues) ;Win10 1703+ If @error Or Not IsArray($aResult) Then Return SetError(1, @error, False) If Not $aResult[0] Then Return SetError(2, __WinAPI_DPI_GetLastError(), False) Return True EndFunc ;==>_WinAPI_SetDialogControlDpiChangeBehavior ;https://learn.microsoft.com/en-us/windows/win32/api/uxtheme/nf-uxtheme-openthemedatafordpi ;The caller owns the returned HTHEME and must release it with _WinAPI_CloseThemeData. Func _WinAPI_OpenThemeDataForDpi($hWnd, $pszClassList, $iDpi) Local $aResult = DllCall("uxtheme.dll", "handle", "OpenThemeDataForDpi", "hwnd", $hWnd, "wstr", $pszClassList, "uint", $iDpi) ;Win10 1703+ If @error Or Not IsArray($aResult) Then Return SetError(1, @error, 0) If Not $aResult[0] Then Return SetError(2, 0, 0) Return $aResult[0] EndFunc ;==>_WinAPI_OpenThemeDataForDpi #EndRegion WinAPI DPI - setters #Region High level ; #FUNCTION# =================================================================================== ; Name ..........: _WinAPI_SetDPIAwareness ; Description ...: Sets the DPI awareness using the best method the running OS supports. ; Syntax ........: _WinAPI_SetDPIAwareness([$iAwarenessLevel = $DPI_LEVEL_PER_MONITOR[, $iMode = $DPI_SCOPE_PROCESS]]) ; Parameters ....: $iAwarenessLevel - unified level, one of $DPI_LEVEL_*: ; 0 = UNAWARE ; 1 = SYSTEM ; 2 = PER_MONITOR (default) ; 3 = PER_MONITOR_V2 (Win10 1703+) ; 4 = UNAWARE_GDISCALED (Win10 1809+) ; Raw DPI_AWARENESS_CONTEXT values (-1..-5) are also ; accepted for backwards compatibility. ; $iMode - $DPI_SCOPE_PROCESS (1, default) or $DPI_SCOPE_THREAD (2) ; Return values .: Success: the current DPI. @extended = 1 means the DPI could not be ; determined and the default of 96 was returned instead. ; Failure: 0 and @error set: ; 1 - no method available or all methods failed (@extended = last Win32 error) ; 10 - thread scope requested but the OS is older than Win10 1607 ; 11 - SetThreadDpiAwarenessContext failed (@extended = @error of the wrapper) ; Remarks .......: Call this before the first GUICreate or any other window access. ; Levels above the OS capability are silently downgraded: V2 -> PER_MONITOR, ; GDISCALED -> UNAWARE. ; In thread scope the previous context is NOT returned. If you need to ; restore it, call _WinAPI_SetThreadDpiAwarenessContext directly. ; Setting the awareness via the application manifest is more robust than ; calling this function, because windows can in principle be created before ; the first script statement runs. ; ============================================================================================== Func _WinAPI_SetDPIAwareness($iAwarenessLevel = $DPI_LEVEL_PER_MONITOR, $iMode = $DPI_SCOPE_PROCESS) ;--- Normalise the requested level --------------------------------------------------- ;Accept raw DPI_AWARENESS_CONTEXT values: -1 -> 0, -2 -> 1, ... -5 -> 4 If $iAwarenessLevel < 0 Then $iAwarenessLevel = (-$iAwarenessLevel) - 1 If $iAwarenessLevel < $DPI_LEVEL_UNAWARE Then $iAwarenessLevel = $DPI_LEVEL_UNAWARE ;Clamp to V2, NOT to GDISCALED: an out-of-range value must not silently select an ;unaware mode. This also closes the out-of-bounds crash the old context map had. If $iAwarenessLevel > $DPI_LEVEL_UNAWARE_GDISCALED Then $iAwarenessLevel = $DPI_LEVEL_PER_MONITOR_V2 ;--- Downgrade to what this OS actually supports -------------------------------------- If $iAwarenessLevel = $DPI_LEVEL_PER_MONITOR_V2 And @OSBuild < $__DPI_BUILD_1703 Then $iAwarenessLevel = $DPI_LEVEL_PER_MONITOR ;GDISCALED is an UNAWARE variant, so its fallback is UNAWARE - not PER_MONITOR If $iAwarenessLevel = $DPI_LEVEL_UNAWARE_GDISCALED And @OSBuild < $__DPI_BUILD_1809 Then $iAwarenessLevel = $DPI_LEVEL_UNAWARE Local Const $iContext = __WinAPI_DPI_LevelToContext($iAwarenessLevel) If $iMode <> $DPI_SCOPE_THREAD Then $iMode = $DPI_SCOPE_PROCESS ;--- Thread scope --------------------------------------------------------------------- If $iMode = $DPI_SCOPE_THREAD Then ;There is no legacy equivalent - per-thread awareness starts with Win10 1607 If @OSBuild < $__DPI_BUILD_1607 Then Return SetError(10, 0, 0) _WinAPI_SetThreadDpiAwarenessContext($iContext) If @error Then Return SetError(11, @error, 0) Local $iThreadDPI = __WinAPI_DPI_QueryCurrent() If Not $iThreadDPI Then Return SetError(0, 1, $__DPI_DEFAULT_DPI) Return $iThreadDPI EndIf ;--- Process scope: descending fallback chain ----------------------------------------- Local $bDone = False, $iLastError = 0 ;Step 1: SetProcessDpiAwarenessContext - Win10 1703+, the only route to V2 / GDISCALED If @OSBuild >= $__DPI_BUILD_1703 Then If _WinAPI_SetProcessDpiAwarenessContext($iContext) Then $bDone = True Else $iLastError = @extended ;ERROR_ACCESS_DENIED: the awareness is already fixed (manifest) and immutable. ;The desired state may well be active already, so accept it instead of failing. If $iLastError = $__DPI_ERROR_ACCESS_DENIED Then $bDone = True EndIf EndIf ;Step 2: SetProcessDpiAwareness - Win8.1+, knows only three levels If Not $bDone And @OSBuild >= $__DPI_BUILD_WIN81 Then Local $iLegacy Switch $iAwarenessLevel Case $DPI_LEVEL_UNAWARE, $DPI_LEVEL_UNAWARE_GDISCALED $iLegacy = $PROCESS_DPI_UNAWARE Case $DPI_LEVEL_SYSTEM $iLegacy = $PROCESS_SYSTEM_DPI_AWARE Case Else $iLegacy = $PROCESS_PER_MONITOR_DPI_AWARE ;PER_MONITOR and V2 both land here EndSwitch If _WinAPI_SetProcessDpiAwareness($iLegacy) Then $bDone = True Else $iLastError = @extended If $iLastError = $__DPI_E_ACCESSDENIED Then $bDone = True ;already set EndIf EndIf ;Step 3: SetProcessDPIAware - Vista+, system aware only. Pointless for unaware levels. If Not $bDone And @OSBuild >= $__DPI_BUILD_VISTA And $iAwarenessLevel <> $DPI_LEVEL_UNAWARE And $iAwarenessLevel <> $DPI_LEVEL_UNAWARE_GDISCALED Then If _WinAPI_SetProcessDPIAware() Then $bDone = True Else $iLastError = @extended If $iLastError = $__DPI_ERROR_ACCESS_DENIED Then $bDone = True ;already set EndIf EndIf ;Step 4: unaware is the default state of every process - nothing to call, nothing failed If Not $bDone And ($iAwarenessLevel = $DPI_LEVEL_UNAWARE Or $iAwarenessLevel = $DPI_LEVEL_UNAWARE_GDISCALED) Then $bDone = True If Not $bDone Then Return SetError(1, $iLastError, 0) ;--- Report the resulting DPI --------------------------------------------------------- Local $iDPI = __WinAPI_DPI_QueryCurrent() ;Awareness was set successfully, so a failed DPI query is not a hard error - flag it ;in @extended and hand back the 96 DPI default. If Not $iDPI Then Return SetError(0, 1, $__DPI_DEFAULT_DPI) Return $iDPI EndFunc ;==>_WinAPI_SetDPIAwareness #EndRegion High level Claude added the comments and fixed some bugs. Please test if everything is working as expected. WildByDesign, mLipok and argumentum 2 1 Please don't send me any personal message and ask for support! I will not reply! Selection of finest graphical examples at Codepen.io The own fart smells best! ✌Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now