DPI Awareness, "new" simple working approach for Win 7/8/10

Share your advanced PureBasic knowledge/code with the community.
MarkOtt
User
User
Posts: 28
Joined: Sat Feb 11, 2017 8:33 pm
Location: Switzerland

DPI Awareness, "new" simple working approach for Win 7/8/10

Post by MarkOtt »

This is a little bit different and much simpler approach for setting DPI Awareness in Windows 7/8/10. It seems to work in any situation, regardless of how the screen scaling has been set within Windows.

It is based on the idea to measure and compare the font size before and after setting the DPI Awareness.
If Windows is already scaling the fonts itself, then the measured font size is the same before and after setting the programs DPI Awareness and we do not need to scale the font size in the program. If the sizes are different then we need to scale the font size ourselfes. The other GUI elements have to be scaled in either way.

I have tested the code in one of my programs on many systems and OS versions and found no failures.
Thanks everybody for testing and refinement....


Edit (to clarify a little bit):
Using PB up to version 5.24 the fonts were scaled automatically correct without scaling them in the program. It was just enough to scale the other GUI elements.
Using PB version 5.45 (and 5.61) the things got more complicated:
Eg. in Windows 10, if I set 125% in the "fixed scaling dialog" (where I can choose 100%, 125%, 150%) then the fonts are not scaled automaticall by Windows, so I have to also scale the fonts in my program.
But Windows 10 scales the fonts automatically correct if I set 125% in the "user scaling dialog" (where it is possible to scale continuously). So Windows 10 font scaling is not behaving consistently, it depends on how it is set by the user. This is what my approach seems to solve (and it works also for Win 7 and 8 ).

Code: Select all

;Measure the font size before setting the programs DPI awareness
Define ncm.NONCLIENTMETRICS
ncm\cbSize = SizeOf(NONCLIENTMETRICS)
If SystemParametersInfo_(#SPI_GETNONCLIENTMETRICS, SizeOf(NONCLIENTMETRICS), ncm, #Null)
  FontNameBefore$ = PeekS(@ncm\lfMessageFont\lfFaceName)
  FontSizeBefore = -PeekL(@ncm\lfMessageFont\lfHeight)    ;in Pixels if minus value (in Points if positive)
EndIf
;MessageRequester("BeforeDpiAwareness", FontNameBefore$ + " " + Str(FontSizeBefore) )

;Set the program to be DPI aware
Prototype proto_SetProcessDPIAware()
Prototype proto_IsProcessDPIAware()
If OpenLibrary(#User32DLL_Handle,"user32.dll")
  SetProcessDPIAware.proto_SetProcessDPIAware = GetFunction(#User32DLL_Handle,"SetProcessDPIAware")
  IsProcessDPIAware.proto_IsProcessDPIAware = GetFunction(#User32DLL_Handle,"IsProcessDPIAware")
  If IsProcessDPIAware() = #False
    SetProcessDPIAware()    ; Windows Vista / Windows Server 2008 and newer
  EndIf
  CloseLibrary(#User32DLL_Handle)
EndIf

;Measure the font size after setting the programs DPI awareness
Define ncm.NONCLIENTMETRICS
ncm\cbSize = SizeOf(NONCLIENTMETRICS)
If SystemParametersInfo_(#SPI_GETNONCLIENTMETRICS, SizeOf(NONCLIENTMETRICS), ncm, #Null)
  FontNameAfter$ = PeekS(@ncm\lfMessageFont\lfFaceName)
  FontSizeAfter = -PeekL(@ncm\lfMessageFont\lfHeight)    ;in Pixels if minus value (in Points if positive)
EndIf
;MessageRequester("AfterDpiAwareness", FontNameAfter$ + " " + Str(FontSizeAfter) )

;Scaling factor for fonts
Global fontScaleFactor.f = FontSizeAfter / FontSizeBefore

;Macro for scaling font sizes
Macro _fontscale(_num_)
  ( Int((_num_) * fontScaleFactor) )
EndMacro

;Scaling factor for other GUI elements
Global dpiScaleFactor.f = 1
Global dpiScaleFactorX.f = 1
Global dpiScaleFactorY.f = 1
_dc = GetDC_(GetDesktopWindow_())
If _dc
  _px = GetDeviceCaps_(_dc, #LOGPIXELSX)
  _py = GetDeviceCaps_(_dc, #LOGPIXELSY)
  ReleaseDC_(#Null, _dc)
  If _px > 0
    dpiScaleFactorX = _px / 96
  EndIf
  If _py > 0
    dpiScaleFactorY = _py / 96
    dpiScaleFactor = _py / 96
  EndIf
EndIf

;Macro for scaling GUI elements
Macro _scale(_num_)
  ( Int((_num_) * dpiScaleFactor) )
EndMacro
Best regards. Markus