Results 1 to 16 of 16

Thread: Monitor Scale Factor

  1. #1

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2018
    Posts
    602

    Monitor Scale Factor

    Hello,

    I need a bullet-proof way to get the Scale Factor of a monitor regardless of DPIAware setting.

    I'm finding I'm getting different results depending on the DPIAware setting,
    but Scale Factor is Scale Factor, and results should be the same either way.

    Does anyone have code that would work regardless of DPIAware setting?

  2. #2
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,996

    Re: Monitor Scale Factor

    The question is why do you think you need that (I'm asking because I guess you most probably are confused about how this DPI issue works).

  3. #3
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,996

    Re: Monitor Scale Factor

    Anyway it is simple: get the DPI for the monitor and divide by 96.

    Edit: I forgot you said "regardless of DPIAware setting".
    That's why I said you must have a misconception on how this works. If the program is declared unaware, then you don't need to know that.

    Edit2: another thing is that you are trying to handle the DPI awareness by API.
    My suggestion is that you first try to make it work with the manifest approach (that has enough difficulty, but at least it is already well proven that it works).
    Once you have everything working as expected with the manifest approach, try to move to the API approach, that are much less known about the issues that it may have.

  4. #4
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,853

    Re: Monitor Scale Factor

    I can't get to it this second, but I've got some code that does this. I'll post it later, if someone else doesn't beat me to it.

    And yeah, I also wouldn't mind knowing what you're trying to accomplish. This does sound like another potential "XY Problem". If we knew what you were trying to do, we might be able to help more.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  5. #5

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2018
    Posts
    602

    Re: Monitor Scale Factor

    The question is why do you think you need that (I'm asking because I guess you most probably are confused about how this DPI issue works).
    I'm finding incorrectly scaled GDI+ renderings in my main program when I run on non-100% scaled monitors, when I set app to DPIAware.
    If I (hard-code) scale by 125% (on a 125% monitor) all is good.
    If I ask for monitor scale on 125% monitor when DPIAware is set I get 100% which is WRONG!

    Anyway it is simple: get the DPI for the monitor and divide by 96.
    Thank you Eduardo - I will try with this

    Btw is there an API or something that would return 96 so I don't hard code this value and then someday Microsoft changes this to 100 for example (as it is for GDI+)

  6. #6
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,996

    Re: Monitor Scale Factor

    Quote Originally Posted by mms_ View Post
    Btw is there an API or something that would return 96 so I don't hard code this value and then someday Microsoft changes this to 100 for example (as it is for GDI+)
    You don't need an API for that, it is a constant.
    So just declare a constant if you don't want to write the number everywhere.

    A Screen at 100% is 96 DPI. There is no need to look for more.

  7. #7
    The Idiot
    Join Date
    Dec 2014
    Posts
    2,721

    Re: Monitor Scale Factor

    I dont use DPI at all.
    u can create your own scale factor by just using a default resolution and compare that with the screen resolution and create a "ratio" value.
    thats how I do it.

  8. #8

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2018
    Posts
    602

    Re: Monitor Scale Factor

    This is my GetMonitorScale function

    Code:
    Private Sub GetMonitorScale(x As Long, y As Long)
    
        Dim hWnd As Long
        Dim hMonitor As Long
        Dim tMonitorInfo As MONITORINFOEX
        Dim hdc As Long
        
        Dim PhysicalResolutionX As Long
        Dim PhysicalResolutionY As Long
        Dim LogicalResolutionX As Long
        Dim LogicalResolutionY As Long
        Dim ScaleFactorX As Single                'aka Device Pixel Ratio (along X axis)
        Dim ScaleFactorY As Single                'aka Device Pixel Ratio (along Y axis)
        
    
        hMonitor = MonitorFromPoint(x, y, MONITOR_DEFAULTTONULL)
    
        tMonitorInfo.cbSize = Len(tMonitorInfo)
        Call GetMonitorInfo(hMonitor, tMonitorInfo)
        
        hdc = CreateDC(tMonitorInfo.szDevice, 0, 0, 0)
    
        PhysicalResolutionX = GetDeviceCaps(hdc, DevCap.DESKTOPHORZRES)
        PhysicalResolutionY = GetDeviceCaps(hdc, DevCap.DESKTOPVERTRES)
        
        LogicalResolutionX = GetDeviceCaps(hdc, DevCap.HORZRES)
        LogicalResolutionY = GetDeviceCaps(hdc, DevCap.VERTRES)
        
        ScaleFactorX = CSng(Round((PhysicalResolutionX / LogicalResolutionX), 2))
        ScaleFactorY = CSng(Round((PhysicalResolutionY / LogicalResolutionY), 2))
        
        DeleteDC (hdc)
        
        Debug.Print tMonitorInfo.szDevice
        Debug.Print "Physical: " & PhysicalResolutionX & " x " & PhysicalResolutionY
        Debug.Print "Logical: " & LogicalResolutionX & " x " & LogicalResolutionY
        Debug.Print "Scale: " & ScaleFactorX & " x " & ScaleFactorY
        Debug.Print "ScaleFactor: " & ScaleFactorX * 100 & "%"
        Debug.Print "-----------------------------------------------------------"
        MsgBox "Monitor #" & numMonitors & " ScaleFactor = " & ScaleFactorX * 100 & "%"
    
    End Sub
    If I set app as unaware
    bAware = SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE)
    above function works

    If I set app as aware
    bAware = SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE)
    above function does not work

  9. #9

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2018
    Posts
    602

    Re: Monitor Scale Factor

    This DPIAware stuff is the most confusing concept I've ever come across!!!

    Eduardo
    Per your suggestion to use the GetDpiForMonitor API, my app works properly from both an EXE and from IDE
    with the caveat that if run from the IDE, the IDE must be shut down after each run of the app.

    This I find very annoying and confusing, because if you make a change to app the run app, it will not execute properly.
    (you must save the change, shut down VB, re-open VB, then run the app and all is good)

    From the MSDN page you linked me to (GetDpiForMonitor) it states:
    This API is not DPI aware and should not be used if the calling thread is per-monitor DPI aware. For the DPI-aware version of this API, see GetDpiForWindow.
    If I do:
    TrueScaleFactor = GetDpiForWindow(Form3.hwnd) / 96
    all works properly: EXE, and IDE without having to shut down between successive runs!!!!

    Thank you!
    Last edited by mms_; Aug 6th, 2022 at 11:55 PM.

  10. #10
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,996

    Re: Monitor Scale Factor

    I wrote this in a message above as a later edition, so you probably didn't see it.
    It was a suggestion:

    Quote Originally Posted by Eduardo- View Post
    Edit2: another thing is that you are trying to handle the DPI awareness by API.
    My suggestion is that you first try to make it work with the manifest approach (that has enough difficulty, but at least it is already well proven that it works).
    Once you have everything working as expected with the manifest approach, try to move to the API approach, that are much less known about the issues that it may have.
    That issue you are describing in the last message seems to be related to the API approach.

    Anyway, I guess you should be able to restore the original awareness before closing the program in the IDE, with the same API.

    If you insist in using the API approach, more that looking for help here you should be teaching us, since you are a pioneer in that field . I know that some have experimented a bit, I think Elroy did something, maybe dilettante too.
    But I always used manifests, and never had that issue.

  11. #11

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2018
    Posts
    602

    Re: Monitor Scale Factor

    That issue you are describing in the last message seems to be related to the API approach.
    Yes, I am doing this so I can control at what point the app gets declared DPIAware as I wanted to do my calculations
    for monitor DPI's etc before Windows starts adjusting these DPIs values because of the DPIAwareness setting.

    I can still try to use as manifest, and see what happens.


    Anyway, I guess you should be able to restore the original awareness before closing the program in the IDE, with the same API.
    Funny, I tried exactly that last night, but it had no effect.
    I put
    bAware = SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE)
    as the last line in my Form_Unload sub,
    but it did nothing to fix the IDE subsequent run issue.
    I guess you can't toggle DPIAwareness????
    I thought I saw a post by Elroy where he toggles this setting, but cannot find it now.
    Last edited by mms_; Aug 7th, 2022 at 08:20 AM.

  12. #12
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,853

    Re: Monitor Scale Factor

    Quote Originally Posted by mms_ View Post
    This DPIAware stuff is the most confusing concept I've ever come across!!!
    For me, that's both true and false.

    In a sense, it's quite simple:
    • If you're not DPI aware, and Windows is set to 100% scaling, then nothing happens. You get pixel-per-pixel scaling.
    • If you're not DPI aware, and Windows is set to non-100% scaling, then Windows will attempt to scale things according to the scale factor. And, as far as I know, this is inclusive of fonts, pictures, and any custom drawing with GDI or GDI+. This is where "blur" can be introduced, but Microsoft has worked on that quite a bit.
    • If you're DPI aware, Windows does not attempt to scale anything (regardless of the Windows scale factor). It assumes that you'll be doing your own scaling, so it doesn't try to do it for you. So, your pictures and graphics are still pixel-per-pixel, regardless of the Windows scale factor.

    Now, because I'm using the word "pixel", there is one clarification. There is also a "display resolution" setting. IMO, it's always advisable to run this at the "recommended" (native resolution) of your monitor. This has nothing to do with DPI or scale settings. It's just a way to "force" the monitor to accept a pixel resolution input that's not the pixel-per-pixel mapping of the monitor. Again, this is separate from the scaling factor.

    It's confusing because, in practice, that stuff is far from straightforward, especially when we take our form borders and title-bars into account. There's just not a great deal of consistency. Here is a thread where I tried to work some of that out, and basically gave up in frustration.

    Quote Originally Posted by Eduardo- View Post
    I know that some have experimented a bit, I think Elroy did something, maybe dilettante too.
    But I always used manifests, and never had that issue.
    I've actually played around with the SetProcessDpiAwarenessContext API call and got it to work with some caveats. Basically, if I call it early (before any forms are showing) and only once, it seems to work. In fact, I regularly call it in an Add-In to my VB6 IDE. Here's the complete code for the Add-In that I keep turned on (code just in the Add-In's designer module):

    Code:
    
    Option Explicit
    '
    Public Enum DPI_AWARENESS_CONTEXT
        Context_Undefined = 0&
        Context_Unaware = -1&                   ' DPI unaware. This window does not scale for DPI changes and is always assumed to have a scale factor of 100% (96 DPI). It will be automatically scaled by the system on any other DPI setting.
        Context_SystemAware = -2&               ' System DPI aware. This window does not scale for DPI changes. It will query for the DPI once and use that value for the lifetime of the process. If the DPI changes, the process will not adjust to the new DPI value. It will be automatically scaled up or down by the system when the DPI changes from the system value.
        Context_PerMonitorAware = -3&           ' Per monitor DPI aware. This window checks for the DPI when it is created and adjusts the scale factor whenever the DPI changes. These processes are not automatically scaled by the system.
        Context_PerMonitorAwareV2 = -4&         ' Also known as Per Monitor v2. An advancement over the original per-monitor DPI awareness mode, which enables applications to access new DPI-related scaling behaviors on a per top-level window basis.
        Context_UnawareGdiScaled = -5&          ' DPI unaware with improved quality of GDI-based content. This mode behaves similarly to DPI_AWARENESS_CONTEXT_UNAWARE, but also enables the system to automatically improve the rendering quality of text and other GDI-based primitives when the window is displayed on a high-DPI monitor.
    End Enum
    #If False Then  ' Intellisense fix.
        Dim Context_Undefined, Context_Unaware, Context_SystemAware, Context_PerMonitorAware, Context_PerMonitorAwareV2, Context_UnawareGdiScaled
    #End If
    Private Declare Function SetProcessDpiAwarenessContext Lib "user32" (ByVal hDpiContext As DPI_AWARENESS_CONTEXT) As Long ' Returns TRUE if the operation was successful, or FALSE.
    '
    Private Sub AddinInstance_OnConnection(ByVal Application As Object, ByVal ConnectMode As AddInDesignerObjects.ext_ConnectMode, ByVal AddInInst As Object, Custom() As Variant)
        If SetProcessDpiAwarenessContext(Context_PerMonitorAwareV2) = 0& Then
            MsgBox "Setting Process DPI Awareness Context FAILED!", vbInformation
        End If
    End Sub
    
    Private Sub AddinInstance_OnDisconnection(ByVal RemoveMode As AddInDesignerObjects.ext_DisconnectMode, Custom() As Variant)
        '
    End Sub
    
    
    
    And, to use this, I do not have the HighDpiAware shim set, as this fouls it up.

    -----------------

    Ok, regarding actually getting the scale factor regardless of what your DPI awareness settings are, I actually reviewed my code, and I've got several conflicting notes, so I'm not going to post anything on that. There are several API calls associated with this, and I think most (if not all) of them are dependent on how you're manifested. Some of those API calls are: GetScaleFactorForMonitor, GetDpiForMonitor, GetDpiForSystem, GetDeviceCaps (with LOGPIXELSX), and examination of the registry key at HKEY_CURRENT_USER, "Control Panel\Desktop", "LogPixels".

    That last one (the registry key) is definitely independent of your manifest, but it's also not per monitor.

    ------------------

    So yeah, in theory, this stuff is simple, but in practice, it's a nightmare.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  13. #13
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,853

    Re: Monitor Scale Factor

    Quote Originally Posted by mms_ View Post
    I thought I saw a post by Elroy where he toggles this setting, but cannot find it now.
    Not toggle. I never got that to work to my satisfaction. However, it can be used (if called before any forms are loaded) as an alternative to the manifest, and only if called once.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  14. #14
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,219

    Re: Monitor Scale Factor

    Quote Originally Posted by mms_ View Post
    ...I wanted to do my calculations for monitor DPI's etc
    before Windows starts adjusting these DPIs values because of the DPIAwareness setting.
    Maybe that's the big misunderstanding all along -
    because only after you have declared your process to be DPI-aware,
    is Windows not "lying to you" anymore (about DPI-Settings and Window-Pixel-Sizes).

    HTH

    Olaf

  15. #15

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2018
    Posts
    602

    Re: Monitor Scale Factor

    Elroy
    Thank you for your answer and code.
    I will study and see what I can use.

    Schmidt
    Maybe that's the big misunderstanding all along -
    because only after you have declared your process to be DPI-aware,
    is Windows not "lying to you" anymore (about DPI-Settings and Window-Pixel-Sizes).
    Everything seems to be working correctly on all computer set-ups I have access to (both VB and non-VB installed set-ups),
    but perhaps you are correct in this.
    Now that everything is working, I will try again to declare app as DPI-aware first thing, and see if app still works properly.
    If it does, that means I should be able to set DPI-aware via manifest also as Eduardo suggests.

    (about DPI-Settings and Window-Pixel-Sizes)
    What do mean by Window-Pixel-Sizes?
    I have a line in my code, that I placed a hard-coded value in (I hate when I do that) and now can't remember what I was thinking.
    gdipRenderScale = MonitorPPI / 100 / 1.2 / MonitorScaleFactor
    Maybe something to do with Window-Pixel-Sizes?
    Last edited by mms_; Aug 7th, 2022 at 01:49 PM.

  16. #16
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,996

    Re: Monitor Scale Factor

    Really, I would not care much if things are working or not working at a given time, but I would care more in understanding how this issue (of DPI awareness) works.

    Even understanding, it is sometimes confusing, because there are many factors.
    The awareness may be unaware (all is at 96 DPI), system aware (aware that can be different DPI setting, but just one and can't change dynamically at any moment), aware per monitor (different monitors can have different DPI, and the DPIs can change at any time), GDI scaling (aware than DPI can be other than 96, but let Windows handle it through automatic scaling).
    Also, the awareness may be declared to the IDE or otherwise you may need to compile the program to test.
    And at the end, you could also surf into the awareness declaration by API...

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width