How do I get the SCALE directly from Windows?

Just starting out? Need help? Post your questions and find answers here.
User avatar
Mijikai
Addict
Addict
Posts: 1520
Joined: Sun Sep 11, 2016 2:17 pm

Re: How do I get the SCALE directly from Windows?

Post by Mijikai »

Maybe like this :>

Gets the current DPI factor of the Monitor our Window is on:

Code: Select all

EnableExplicit

Procedure.i GetMonitorDPI(Window.i,*FactorX.Double,*FactorY.Double)
  Protected *api
  Protected *proc
  Protected hmonitor.i
  Protected dpi_x.i
  Protected dpi_y.i
  *api = Ascii("GetDpiForMonitor")
  If *api
    *proc = GetProcAddress_(LoadLibrary_("shcore.dll"),*api)
    FreeMemory(*api)
    If *proc
      hmonitor = MonitorFromWindow_(WindowID(Window),#MONITOR_DEFAULTTONEAREST)
      If CallFunctionFast(*proc,hmonitor,#Null,@dpi_x,@dpi_y) = #S_OK
        *FactorX\d = dpi_x / 96.0
        *FactorY\d = dpi_y / 96.0
        ProcedureReturn #True
      EndIf
    EndIf
  EndIf
  ProcedureReturn #False
EndProcedure

Procedure.i Main()
  Protected factor_x.d
  Protected factor_y.d
  If OpenWindow(0,0,0,960,600,#Null$,#PB_Window_SystemMenu|#PB_Window_ScreenCentered|#PB_Window_MinimizeGadget)
    Debug GetMonitorDPI(0,@factor_x,@factor_y)
    Debug factor_x
    Debug factor_y
    Repeat
      Select WaitWindowEvent()
        Case #PB_Event_CloseWindow
          Break
      EndSelect
    ForEver
    CloseWindow(0)  
  EndIf  
  ProcedureReturn #Null
EndProcedure

Main()

End
breeze4me
Enthusiast
Enthusiast
Posts: 654
Joined: Thu Mar 09, 2006 9:24 am
Location: S. Kor

Re: How do I get the SCALE directly from Windows?

Post by breeze4me »

GetDpiForMonitor does not work properly. Because PB does not support DPI scaling per monitor.
For example, if the main monitor is 100% and the second monitor is 125%, GetDpiForMonitor always returns 100%.

To use GetDpiForMonitor, you must turn off DPI awareness in the compiler settings and force the process to set DPI scaling per monitor.

The code below seems to work well on Windows 10. It's not tested on other versions of Windows.
Turn off DPI awareness in compiler settings and run.

Code: Select all

#PROCESS_DPI_UNAWARE = 0
#PROCESS_SYSTEM_DPI_AWARE = 1
#PROCESS_PER_MONITOR_DPI_AWARE = 2

#DPI_AWARENESS_CONTEXT                                = 0
#DPI_AWARENESS_CONTEXT_UNAWARE                        = #DPI_AWARENESS_CONTEXT - 1
#DPI_AWARENESS_CONTEXT_SYSTEM_AWARE                   = #DPI_AWARENESS_CONTEXT - 2
#DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE              = #DPI_AWARENESS_CONTEXT - 3
#DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2           = #DPI_AWARENESS_CONTEXT - 4


;Return value: 1507, 1511, 1607, 1703, 1709, 1803, 1809, 1903, 1909, 2004, 2009(20H2) etc.
Procedure.s GetWindowReleaseID()
  Protected Buffer.s{16}, Type = #REG_SZ, Size = 15
  If OSVersion() >= #PB_OS_Windows_10
    If SHGetValue_(#HKEY_LOCAL_MACHINE, "SOFTWARE\Microsoft\Windows NT\CurrentVersion", "ReleaseID", @Type, @Buffer, @Size) = #ERROR_SUCCESS
      ProcedureReturn Buffer
    EndIf
  EndIf
  ProcedureReturn ""
EndProcedure

Procedure MonitorEnumProc(hMonitor, hDC, *Rect, lParam)
  Protected DpiX, DpiY
  Protected LibShcore = OpenLibrary(#PB_Any, "Shcore.dll")
  
  If LibShcore
    If hMonitor
      If CallFunction(LibShcore, "GetDpiForMonitor", hMonitor, #MDT_EFFECTIVE_DPI, @DpiX, @DpiY) = #S_OK
        Debug "DPI:   x= " + DpiX + " ,  y= " + DpiY
        Debug "Scaling factor:   x= " + StrD(DpiX/96) + " ,  y= " + StrD(DpiY/96)
        Debug "-------------------------------------------"
      EndIf
    EndIf
    
    CloseLibrary(LibShcore)
  EndIf
  
  ProcedureReturn #True
EndProcedure

Procedure GetDPImonitors()
  Protected LibShcore = OpenLibrary(#PB_Any, "Shcore.dll")
  Protected LibUser32 = OpenLibrary(#PB_Any, "User32.dll")
  Protected hDC, Awareness, Result, OS = OSVersion()
  Protected rlsid = Val(Left(GetWindowReleaseID(), 2))
  
  If OS >= #PB_OS_Windows_8_1
    
    If CallFunction(LibShcore, "GetProcessDpiAwareness", GetCurrentProcess_(), @Awareness) = #S_OK
      
      If Awareness = #PROCESS_DPI_UNAWARE
        
        If (OS = #PB_OS_Windows_10 And rlsid >= 17) Or OS = #PB_OS_Windows_Future    ; OS >= #PB_OS_Windows_11
          
          If CallFunction(LibUser32, "SetProcessDpiAwarenessContext", #DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)
            Debug "Windows 10 1703+"
            
            EnumDisplayMonitors_(0, 0, @MonitorEnumProc(), 0)
            
            CallFunction(LibUser32, "SetProcessDpiAwarenessContext", #DPI_AWARENESS_CONTEXT_UNAWARE)
            Result = 1
            
          ;Else
          ;  If GetLastError_() = #ERROR_ACCESS_DENIED
          ;    Debug "System DPI awareness is applied. Cannot change."
          ;  EndIf
          EndIf
          
        Else
          
          ; If OS = #PB_OS_Windows_10 And rlsid = 16
          ;   CallFunction(LibUser32, "SetProcessDpiAwarenessContext", #DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE)
          ;    ......
          ;   CallFunction(LibUser32, "SetProcessDpiAwarenessContext", #DPI_AWARENESS_CONTEXT_UNAWARE)
          
          ; If OS < #PB_OS_Windows_10 Or (OS = #PB_OS_Windows_10 And rlsid < 16)
          If CallFunction(LibShcore, "SetProcessDpiAwareness", #PROCESS_PER_MONITOR_DPI_AWARE) = #S_OK
            EnumDisplayMonitors_(0, 0, @MonitorEnumProc(), 0)
            
            CallFunction(LibShcore, "SetProcessDpiAwareness", #PROCESS_DPI_UNAWARE)
            Result = 1
            
          ;Else
          ;  Debug "System DPI awareness is applied. Cannot change."
          EndIf
          
        EndIf
        
      Else  ;PB System DPI awareness.
        
        EnumDisplayMonitors_(0, 0, @MonitorEnumProc(), 0)
        Result = 1
      EndIf
    EndIf
    
  EndIf
  
  If Result = 0
    hDC = GetDC_(0)
    If hDC
      Debug GetDeviceCaps_(hDC, #LOGPIXELSX) / 96.0
      Debug GetDeviceCaps_(hDC, #LOGPIXELSY) / 96.0
      ReleaseDC_(0, hDC)
    EndIf
  EndIf
  
  If LibShcore : CloseLibrary(LibShcore) : EndIf
  If LibUser32 : CloseLibrary(LibUser32) : EndIf
EndProcedure

GetDPImonitors()
Windows 10 1703+
DPI: x= 96 , y= 96
Scaling factor: x= 1 , y= 1
-------------------------------------------
DPI: x= 120 , y= 120
Scaling factor: x= 1.25 , y= 1.25
-------------------------------------------
User avatar
Mijikai
Addict
Addict
Posts: 1520
Joined: Sun Sep 11, 2016 2:17 pm

Re: How do I get the SCALE directly from Windows?

Post by Mijikai »

Fixed my code from before now it doesnt matter if DPI aware in the IDE is set or not.

Code: Select all

EnableExplicit

;Minimum supported client Windows 8.1

Procedure.i GetMonitorDPI(Window.i,*FactorX.Double,*FactorY.Double)
  Protected hlib.i
  Protected *api
  Protected *proc
  Protected hmonitor.i
  Protected dpi_x.i
  Protected dpi_y.i
  hlib = LoadLibrary_("shcore.dll")
  *api = Ascii("SetProcessDpiAwareness")
  If *api
    *proc = GetProcAddress_(hlib,*api)
    FreeMemory(*api)
    If *proc
      hmonitor = CallCFunctionFast(*proc,2);<- make process (per monitor) dpi aware if it isnt
      If hmonitor = #S_OK Or hmonitor = #E_ACCESSDENIED
        *api = Ascii("GetDpiForMonitor")
        If *api
          *proc = GetProcAddress_(hlib,*api)
          FreeMemory(*api)
          If *proc
            hmonitor = MonitorFromWindow_(WindowID(Window),#MONITOR_DEFAULTTONEAREST)
            If CallFunctionFast(*proc,hmonitor,#Null,@dpi_x,@dpi_y) = #S_OK
              *FactorX\d = dpi_x / 96.0
              *FactorY\d = dpi_y / 96.0
              ProcedureReturn #True
            EndIf
          EndIf
        EndIf
      EndIf
    EndIf
  EndIf
  ProcedureReturn #False
EndProcedure

Procedure.i Main()
  Protected factor_x.d
  Protected factor_y.d
  If OpenWindow(0,0,0,960,600,#Null$,#PB_Window_SystemMenu|#PB_Window_ScreenCentered|#PB_Window_MinimizeGadget)
    Debug GetMonitorDPI(0,@factor_x,@factor_y)
    Debug factor_x
    Debug factor_y
    Repeat
      Select WaitWindowEvent()
        Case #PB_Event_CloseWindow
          Break
      EndSelect
    ForEver
    CloseWindow(0)  
  EndIf  
  ProcedureReturn #Null
EndProcedure

Main()

End
Post Reply