Page 1 of 2
Win 8.1+ AND PB 5.40+: Fonts too small in DPI-aware progs
Posted: Sun Oct 04, 2015 9:09 pm
by Little John
Hi,
when making a program DPI-aware on Windows 10, PB 5.31 and PB 5.40 beta 8 behave differently as for font size.
//edit 2017-06-02:
See this post below with code that seems to solve the problem.
//edit 2017-08-11:
Even better, see Blue's improved version on page 2. Many thanks for that!
The following test code is based on
code by Rescator, but uses the new (i.e. Windows 8.1+) functions
GetProcessDpiAwareness() and
SetProcessDpiAwareness().
Code: Select all
; -- Windows 8.1+ (tested on Windows 10)
EnableExplicit
; Constants for Get/SetProcessDpiAwareness()
Enumeration
#Process_DPI_Unaware
#Process_System_DPI_Aware
#Process_Per_Monitor_DPI_Aware
EndEnumeration
Prototype.i pGetProcessDpiAwareness (hprocess.i, *level)
Prototype.i pSetProcessDpiAwareness (level.i)
Global.f g_ScaleDPIx=1.0, g_ScaleDPIy=1.0
Procedure InitScaleDPI()
Protected GetProcessDpiAwareness.pGetProcessDpiAwareness
Protected SetProcessDpiAwareness.pSetProcessDpiAwareness
Protected.i shcore, hdc, dpiaware=#Process_DPI_Unaware
; Only use this in EXEs, as DLLs inherit DPI from the calling process.
CompilerIf #PB_Compiler_ExecutableFormat = #PB_Compiler_Executable
shcore = OpenLibrary(#PB_Any, "shcore.dll")
If shcore
GetProcessDpiAwareness = GetFunction(shcore, "GetProcessDpiAwareness")
If GetProcessDpiAwareness
If GetProcessDpiAwareness(#Null, @ dpiaware) = #S_OK
Debug "Get: OK"
EndIf
Debug "DPI-awareness: " + dpiaware
EndIf
; If the exe is allready dpi aware (like through a manifest), then we skip using
; the SetProcessDpiAwareness function.
If dpiaware = #Process_DPI_Unaware
SetProcessDpiAwareness = GetFunction(shcore, "SetProcessDpiAwareness")
If SetProcessDpiAwareness
If SetProcessDpiAwareness(#Process_System_DPI_Aware) = #S_OK
Debug "Set: OK"
EndIf
EndIf
EndIf
CloseLibrary(shcore)
EndIf
CompilerEndIf
hdc = GetDC_(#Null) ; get handle to the device context for the entire screen
If hdc
g_ScaleDPIx = GetDeviceCaps_(hdc, #LOGPIXELSX) / 96.0 ; 96 is the default DPI value on Windows
g_ScaleDPIy = GetDeviceCaps_(hdc, #LOGPIXELSY) / 96.0
ReleaseDC_(#Null, hdc)
EndIf
EndProcedure
Macro DPIx (_x_)
(_x_) * g_ScaleDPIx
EndMacro
Macro DPIy (_y_)
(_y_) * g_ScaleDPIy
EndMacro
Define font
InitScaleDPI()
font = LoadFont(#PB_Any, "Segoe UI", 9, #PB_Font_HighQuality)
If font = 0
MessageRequester("Error", "Unable to load font.")
End
EndIf
SetGadgetFont(#PB_Default, FontID(font))
If OpenWindow(#PB_Any, #PB_Ignore, #PB_Ignore, DPIx(180), DPIy(110), "PB " + StrF(#PB_Compiler_Version/100, 2)) = 0
MessageRequester("Error", "Unable to open main window.")
End
EndIf
TextGadget(#PB_Any, DPIx(20), DPIy(20), DPIx(80), DPIy(30), "ScaleDPIy: " + StrF(g_ScaleDPIy, 2))
TextGadget(#PB_Any, DPIx(20), DPIy(60), DPIx(80), DPIy(30), "Segoe UI 9 pt")
Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
Here are screenshots of the created windows with PB 5.31 and PB 5.40 beta 8 on DPI settings 125% and 200%. The screenshots also contain the corresponding control panel dialog, which shows example text in the proper size for comparison.
Why do PB 5.31 and PB 5.40 beta 8 produce different results here?
Is there something wrong with my code, or is this a bug in PB 5.40 beta 8?
PS: Without calling any DPI related Windows API functions in the code, everything works as expected.
Re: [5.40 beta 8] Fonts too small in DPI-aware programs
Posted: Sun Oct 04, 2015 9:49 pm
by GPI
I think pb must change something with the LoadFont()-function
Sorry, i can't test it here, but when you outcommend this section:
Code: Select all
;font = LoadFont(#PB_Any, "Segoe UI", 9, #PB_Font_HighQuality)
;If font = 0
; MessageRequester("Error", "Unable to load font.")
; End
;EndIf
;SetGadgetFont(#PB_Default, FontID(font))
Does it work then?
Maybe you can try to load a font with the windows-api direct and use then the handle with SetGadgetFont().
Re: [5.40 beta 8] Fonts too small in DPI-aware programs
Posted: Mon Oct 05, 2015 6:24 am
by Little John
Hi GPI,
thanks for your reply.
GPI wrote:Sorry, i can't test it here, but when you outcommend this section:
Code: Select all
;font = LoadFont(#PB_Any, "Segoe UI", 9, #PB_Font_HighQuality)
;If font = 0
; MessageRequester("Error", "Unable to load font.")
; End
;EndIf
;SetGadgetFont(#PB_Default, FontID(font))
Does it work then?
Then the results are the same with PB 5.31 and PB 5.40 beta 8: Fonts are always too small for higher DPI values.
GPI wrote:Maybe you can try to load a font with the windows-api direct and use then the handle with SetGadgetFont().
That's a good idea. Unfortunately, I don't know how to do so.
Re: [5.40 beta 8] Fonts too small in DPI-aware programs
Posted: Sun Mar 12, 2017 5:56 am
by Thunder93
Using PureBasic 5.60 Stable under Windows 10.
The code provided, don't set the correct font size. The correct font size would be what is specified with my Windows. What is being set by the code is much too small. Easily addressed, and something else that's not just specific to your code.
Using this latest API or the previous API for setting DPI awareness. I can't seem to get PureBasic to apply the ClearType to the font. Is this a bug with PB itself and #PB_Font_HighQuality usage when making your apps DPI Aware? .. in any case It looks weird, and I've tried to apply ClearType .. as it's used by default on my Windows. However to no avail!
Re: [5.40 beta 8] Fonts too small in DPI-aware programs
Posted: Sun Mar 12, 2017 8:32 am
by Thunder93
Just remembered something, I wasn't switching to TrueType version of Windows default font, when making the application DPI-Aware. My bad! Now she's okay

Re: [5.40 beta 8] Fonts too small in DPI-aware programs
Posted: Wed May 31, 2017 1:28 pm
by Rinzwind
So what is the fix to make the code in the first post work in both Windows 7 and 10? On 10 it is way too small... Also, when will DPI awareness 'just work'? Its annoying to have to add helper function everywhere and it does not work with the designer generated code...
Re: [5.40 beta 8] Fonts too small in DPI-aware programs
Posted: Wed May 31, 2017 2:19 pm
by chi
Rinzwind wrote:So what is the fix to make the code in the first post work in both Windows 7 and 10?
Code: Select all
If g_ScaleDPIy <= 1.25
font = LoadFont(#PB_Any, "Segoe UI", 9, #PB_Font_HighQuality)
Else
font = LoadFont(#PB_Any, "Segoe UI", 9 * g_ScaleDPIy, #PB_Font_HighQuality)
EndIf
Re: [5.40 beta 8] Fonts too small in DPI-aware programs
Posted: Wed May 31, 2017 3:07 pm
by Rinzwind
The thing is that loadfont works differently on Windows 7 compared to 10 (see also the opening post). There the font is scaled correctly without any modification. In windows 10 its not. So wheres that comming from? Youre code fixes it in Windows 10, but makes the font too large in Windows 7. Wonder why "automatic" dpi support is not added yet. In an old interview Fred answers "soon" to the question when its available. Do we really need another hack that checks the os version for this too? And what about Windows 8?
Re: [5.40 beta 8] Fonts too small in DPI-aware programs
Posted: Wed May 31, 2017 4:36 pm
by Little John
Oops, I had almost forgotten about this thread.
Solution
This is what I'm doing on
Windows for quite some time now. As far as I can see, it works fine.
//edit 2017-06-24
- Inside the module:
Now even when compiled with PB versions < 5.40, the code is able to reliably detect whether it is running on Windows 8.1 or newer.
- Demo code:
Simplified
Code: Select all
; successfully tested with all combinations of
; - Windows XP, 7, 10 Creators update
; - DPI 125%, 150%
; - PB 5.31, 5.44 LTS, 5.60
DeclareModule Std
; [...]
Declare.f FactorDPIx()
Declare.f FactorDPIy()
EndDeclareModule
Module Std
EnableExplicit
; [...]
CompilerIf #PB_Compiler_Version < 540
Structure RTL_OSVERSIONINFOEXW
dwOSVersionInfoSize.l
dwMajorVersion.l
dwMinorVersion.l
dwBuildNumber.l
dwPlatformId.l
szCSDVersion.u[128]
wServicePackMajor.w
wServicePackMinor.w
wSuiteMask.w
wProductType.b
wReserved.b
EndStructure
Prototype.i pRtlGetVersion (*ver.RTL_OSVERSIONINFOEXW)
#STATUS_SUCCESS = 0
Procedure.i _IsWindows81OrNewer()
; In PB 5.31, OSVersion() returns e.g. 90 on Windows 8 and also
; on Windows 10. So with older PB versions that function can't
; be used to check the current Windows version reliably.
; Therefore in order to check whether the current OS is Windows
; 8.1 or newer, this self-written procedure is used here.
;
; out: #True if the currently running OS is Windows 8.1 or newer,
; #False otherwise
Protected ver.RTL_OSVERSIONINFOEXW
Protected RtlGetVersion.pRtlGetVersion
Protected hDLL.i
ver\dwOSVersionInfoSize = SizeOf(ver)
hDLL = OpenLibrary(#PB_Any, "ntdll.dll")
If hDLL
RtlGetVersion = GetFunction(hDLL, "RtlGetVersion")
CloseLibrary(hDLL)
EndIf
If RtlGetVersion = 0 Or RtlGetVersion(@ ver) <> #STATUS_SUCCESS
ProcedureReturn #False
EndIf
If ver\dwPlatformId <> #VER_PLATFORM_WIN32_NT
ProcedureReturn #False
EndIf
If (ver\dwMajorVersion = 6 And ver\dwMinorVersion = 3) Or
ver\dwMajorVersion > 6
ProcedureReturn #True
EndIf
ProcedureReturn #False
EndProcedure
CompilerEndIf
Prototype.i pIsProcessDPIAware()
Prototype.i pSetProcessDPIAware()
Define s_InitDPI.i=#False, s_ScaleDPIx.f=1.0, s_ScaleDPIy.f=1.0
Procedure _InitScaleDPI()
; Windows 5.0 or higher needed for minimum functionality of this procedure
; [modified after <http://www.purebasic.fr/english/viewtopic.php?f=12&t=40507>, 2010-01-02
; see also <http://msdn.microsoft.com/en-us/library/windows/desktop/dd464660%28v=vs.85%29.aspx>
; <http://blogs.msdn.com/b/oldnewthing/archive/2004/07/14/182971.aspx>
; <http://www.purebasic.fr/english/viewtopic.php?p=462177#p462177>
; <http://www.purebasic.fr/english/viewtopic.php?f=13&t=62043>]
Shared s_InitDPI, s_ScaleDPIx, s_ScaleDPIy
Protected IsProcessDPIAware.pIsProcessDPIAware
Protected SetProcessDPIAware.pSetProcessDPIAware
Protected.i user32, hdc, dlgFont, dpiaware=#False
CompilerIf #PB_Compiler_ExecutableFormat = #PB_Compiler_Executable
; Only use this in EXEs, as DLLs inherit DPI from the calling process.
; This part is Windows 6.x+ only (Vista and newer) and must be done before using
; GetDeviceCaps().
user32 = OpenLibrary(#PB_Any, "user32.dll")
If user32
IsProcessDPIAware = GetFunction(user32, "IsProcessDPIAware")
If IsProcessDPIAware
dpiaware = IsProcessDPIAware()
EndIf
CompilerIf #PB_Compiler_IsMainFile
Debug "DPI-aware: " + dpiaware
CompilerEndIf
; If the exe is allready DPI aware (like through a manifest), then we skip using
; the set DPI aware function.
If dpiaware = #False
SetProcessDPIAware = GetFunction(user32, "SetProcessDPIAware")
If SetProcessDPIAware
If SetProcessDPIAware()
CompilerIf #PB_Compiler_IsMainFile
Debug "Set DPI: OK"
CompilerEndIf
EndIf
EndIf
EndIf
CloseLibrary(user32)
EndIf
CompilerEndIf
hdc = GetDC_(#Null) ; get handle to the device context for the entire screen
If hdc
s_ScaleDPIx = GetDeviceCaps_(hdc, #LOGPIXELSX) / 96.0 ; 96 is the default DPI value on Windows.
s_ScaleDPIy = GetDeviceCaps_(hdc, #LOGPIXELSY) / 96.0
ReleaseDC_(#Null, hdc)
EndIf
CompilerIf #PB_Compiler_Version < 540
If _IsWindows81OrNewer()
CompilerIf #PB_Compiler_IsMainFile
Debug "Windows 8.1 or newer"
CompilerEndIf
; Here the font sizes are adjusted automatically.
dlgFont = LoadFont(#PB_Any, "Segoe UI", 9.0, #PB_Font_HighQuality)
If dlgFont
SetGadgetFont(#PB_Default, FontID(dlgFont))
EndIf
EndIf
CompilerElse
If OSVersion() >= #PB_OS_Windows_8_1
CompilerIf #PB_Compiler_IsMainFile
Debug "Windows 8.1 or newer"
CompilerEndIf
; Here the font sizes are not adjusted automatically.
dlgFont = LoadFont(#PB_Any, "Segoe UI", 9.0*s_ScaleDPIy, #PB_Font_HighQuality)
If dlgFont
SetGadgetFont(#PB_Default, FontID(dlgFont))
EndIf
EndIf
CompilerEndIf
s_InitDPI = #True
EndProcedure
Procedure.f FactorDPIx()
Shared s_InitDPI, s_ScaleDPIx
If s_InitDPI = #False
_InitScaleDPI()
EndIf
ProcedureReturn s_ScaleDPIx
EndProcedure
Procedure.f FactorDPIy()
Shared s_InitDPI, s_ScaleDPIy
If s_InitDPI = #False
_InitScaleDPI()
EndIf
ProcedureReturn s_ScaleDPIy
EndProcedure
EndModule
CompilerIf #PB_Compiler_IsMainFile
; -- Demo
EnableExplicit
Global g_ScaleDPIx.f, g_ScaleDPIy.f
g_ScaleDPIx = Std::FactorDPIx()
g_ScaleDPIy = Std::FactorDPIy()
Macro DPIx (_x_)
(_x_) * g_ScaleDPIx
EndMacro
Macro DPIy (_y_)
(_y_) * g_ScaleDPIy
EndMacro
If OpenWindow(#PB_Any, #PB_Ignore, #PB_Ignore, DPIx(120), DPIy(70), "" ) = 0
MessageRequester("Fatal error", "Can't open main window.")
End
EndIf
TextGadget(#PB_Any, DPIx(30), DPIy(20), DPIx(60), DPIy(20), "DPI: " + StrF(100*g_ScaleDPIy,0) + " %", #PB_Text_Border)
Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
CompilerEndIf
Re: [5.40 beta 8] Fonts too small in DPI-aware programs
Posted: Wed May 31, 2017 6:42 pm
by chi
It still doesn't work on Windows 7 using 150% (no XP dpi). Must have something to do with "Use Windows XP style DPI scaling"...
Btw: MS reworked the DPI system in Windows 8.1 (
read here), that's why it's working on Windows 10.
Re: [5.40 beta 8] Fonts too small in DPI-aware programs
Posted: Wed May 31, 2017 7:24 pm
by chi
Ok, I think I found the problem... There seems to be a difference between activating DPI-awareness via code or manifest! Little John's last code only works on Win7 (150%, No XP DPI) if the awareness was set via manifest:
Code: Select all
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" >
<assemblyIdentity
version="1.0.0.0"
processorArchitecture="X86"
name="CompanyName.ProductName.YourApp"
type="win32" />
<description></description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="X86"
publicKeyToken="6595b64144ccf1df"
language="*" />
</dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges>
<requestedExecutionLevel
level="asInvoker"
uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>true</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
</assembly>
Maybe it's time to add a new "Application is DPI aware" checkbox to the compiler options

Re: [5.40 beta 8] Fonts too small in DPI-aware programs
Posted: Fri Jun 02, 2017 7:02 pm
by Little John
chi wrote:Btw: MS reworked the DPI system in Windows 8.1
That's why the code in my previous post contains the line
chi wrote:Little John's last code only works on Win7 (150%, No XP DPI) if the awareness was set via manifest
That is contrary to my experience.
The code in my previous post worked fine here on all combinations of Windows XP, 7, and 10 with PureBasic 5.31 and 5.44. And I
never have set DPI awareness via manifest. I just used that code.
Re: Win 8.1+ AND PB 5.40+: Fonts too small in DPI-aware prog
Posted: Sun Jun 04, 2017 3:30 pm
by chi
Maybe because I'm on PB 5.60?! Did you check your code on 5.60?
edit: I just checked multiple versions of PB (x86 on Windows 7 x64):
5.31: OK
5.41 LTS: NO
5.42 LTS: NO
5.50: NO
5.60: NO
Re: Win 8.1+ AND PB 5.40+: Fonts too small in DPI-aware prog
Posted: Fri Jul 21, 2017 4:06 am
by Blue
@ Little John
Greetings Little John !
Hope summer agrees with you.
It sure does with my old bones.
Down to business :
I very much like your short and sweet macro approach to the scaling of coordinates and sizes.
It makes for clear, concise and smart looking code. (I quite admire your Mastery of macros).
I'm just wondering why you insist on maintaining separate scalings for X and Y values.
From everything I've read on the subject, DPI values, whether horizontal or vertical, are always identical.
So why not simplify everything and use only a single value — and corresponding macro — for all axis ?
I figure there must be a reason behind the extra work, but i'm not quite able to see it yet.
Any explanation ?
Re: Win 8.1+ AND PB 5.40+: Fonts too small in DPI-aware prog
Posted: Fri Jul 21, 2017 5:54 am
by Little John
Hello Blue,
I'm pleased to "see" you! And thanks for your kind words.
Yesterday, we had a little discussion about whether or not what is currently happening here in Berlin should actually be called "summer".

However, I am happy with it.
Blue wrote:I'm just wondering why you insist on maintaining separate scalings for X and Y values.
From everything I've read on the subject, DPI values, whether horizontal or vertical, are always identical.
So why not simplify everything and use only a single value — and corresponding macro — for all axis ?
I was wondering why, too.

Then some very knowledgeable people such as luis and Danilo
told me, that it's not safe to assume that DPI factors are always the same on x and y axis.