WinBatch® Technical Support Forum

All Things WinBatch => WinBatch => Topic started by: MrLeadFoot on March 05, 2025, 10:09:10 PM

Title: Get Display Scale
Post by: MrLeadFoot on March 05, 2025, 10:09:10 PM
Is there a way to get the display scale factor such as 100%, 125%, 150%, etc. using WinBatch? I don't need to manipluate it, I just want to be able to know what it is so I can size and positioning windows appropriately for different scale factors.

Thanks in advance.
Title: Re: Get Display Scale
Post by: kdmoyers on March 06, 2025, 02:38:41 AM
This is not really what you asked, but,
I imagine some experimenting with the WinMetrics function might allow you to infer the setting.
Like, under different scales, does the value returned by WinMetrics(-6) change?
Title: Re: Get Display Scale
Post by: spl on March 06, 2025, 06:55:07 AM
Possibly could use WMI....

objWMIService.ExecQuery("SELECT * FROM Win32_DesktopMonitor",,48) get screenheight/screenwidth properties and do the math.

or Win32_VideoController class and VideoModeDescription property.

Title: Re: Get Display Scale
Post by: JTaylor on March 06, 2025, 07:01:08 AM
If you can't find a different answer, here is my usual response :-)

http://www.jtdata.com/anonymous/wbOmnibus.zip

Look at snScreenScale() in the Help.  Can't recall why I added it.  If it was just convenient of there was not a good way via WinBatch.

Here is the internal code in case that prompt a direct WinBatch solution.  You can see I made an assumption with the 96 DPI default.  That covers most situations.  High-end monitors might be different.  If you run into that situation let me know.  I probably should tweak this function anyway to allow one to submit the DPI and default to 96.  It is on my list.

LONG CDOM::snScreenScale(HWND hWnd, HINSTANCE hInst, LPLONG lpLong, LPVIPERVAR lpvvArg, LONG nArgCount)
{

    HWND ControlHandle = reinterpret_cast<HWND>(lpvvArg
    //int format = lpvvArg[1].x;
    HDC hdc = GetDC(ControlHandle);
    double lx = GetDeviceCaps(hdc, LOGPIXELSX);
    lx = lx / 96.00;
    return ReturnResult(lx);
}

Jim

Title: Re: Get Display Scale
Post by: td on March 06, 2025, 07:41:17 AM
This topic has been covered before
https://forum.winbatch.com/index.php?msg=16302 (https://forum.winbatch.com/index.php?msg=16302)

A repost of the example in the link:

;; Note: If WinBatch was not manifested to be DPI aware
;; this would not work.  But it is so it does.

hDC = DllCall("User32.dll", long_ptr:"GetDC", lpnull)

LOGPIXELSX = 88
nPixPerInchX = DllCall("Gdi32.dll", long:"GetDeviceCaps", long:hDC, long:LOGPIXELSX)

;; 96 is the default Windows scaling.
nScallingPercent = (nPixPerInchX/96.0) * 100
x =  DllCall("User32.dll", long:"ReleaseDC", lpnull, long:hDC)
Message("Desktop Scalling",  nScallingPercent:"%%")

;; This would require Windows 10 - 1604 or later
;;nPixPerInchX10 = DllCall("User32.dll", long:"GetDpiForWindow", long:DllHwnd(""))

exit

Title: Re: Get Display Scale
Post by: JTaylor on March 06, 2025, 08:03:52 AM
Sounds like it is covered but for what it is worth, I just posted an update which allows you to specify default DPI.

Jim
Title: Re: Get Display Scale
Post by: kdmoyers on March 06, 2025, 08:07:29 AM
handy, thanks Tony!!

interesting behavior of the GetDpiForWindow call:
It seems to not really look at which monitor the specified window is displayed on.  I have two displays, primary one at 100% the other at 125%, and it doesn't matter where the window is located, the answer is always 100%.  So watch out for that if you are running two displays.
Title: Re: Get Display Scale
Post by: td on March 06, 2025, 08:45:11 AM
Quote from: kdmoyers on March 06, 2025, 08:07:29 AMhandy, thanks Tony!!

interesting behavior of the GetDpiForWindow call:
It seems to not really look at which monitor the specified window is displayed on.  I have two displays, primary one at 100% the other at 125%, and it doesn't matter where the window is located, the answer is always 100%.  So watch out for that if you are running two displays.


You see that result because the WinBatch exe is not manifested as being per-monitor DPI aware. If Wiindows is behaving itself, you should always get the primary monitor's DPI.
Title: Re: Get Display Scale
Post by: td on March 06, 2025, 10:01:21 AM
What happens when you run the following on your two displays out of curiosity?

Major   = WinVersion(1)
BuildNo = WinVersion(2)

if Major >= 10 && BuildNo >= 1607
   PROCESS_DPI_UNAWARE = 0
   PROCESS_SYSTEM_DPI_AWARE = -1
   PROCESS_PER_MONITOR_DPI_AWARE = -2
   Context = DllCall("User32.dll", long:"SetThreadDpiAwarenessContext", long:PROCESS_PER_MONITOR_DPI_AWARE)
   nPixPerInchX10 = DllCall("User32.dll", long:"GetDpiForSystem")   ;, long:DllHwnd("")
   DllCall("User32.dll", long:"SetThreadDpiAwarenessContext", long:Context)
   nScallingPercent = (nPixPerInchX10/96.0)*100
   Message("Desktop Scalling",  nScallingPercent:"%%")
 endif
exit

[edit] It might be better to try it with "GetDpiForWindow" instead of "GetDpiForSystem".
Title: Re: Get Display Scale
Post by: kdmoyers on March 06, 2025, 10:15:55 AM
it says "Desktop scalling 100%" from either monitor.

(( to test, I move the Studio window to primary monitor, click run,
then move to Second monitor, click run.))

now trying with getdpi for window...
Title: Re: Get Display Scale
Post by: kdmoyers on March 06, 2025, 10:19:02 AM
using GetDpiForWindow, same tests, same result.
Title: Re: Get Display Scale
Post by: kdmoyers on March 06, 2025, 10:33:09 AM
Here's a thing: what if I compile the program? would that matter?

I put this code at top, to allow setting which monitor it was running on,

boxopen("screen scaling","move me with 5 seconds")
timedelay(5)

but it made no difference.  Still says 100% in all cases.
Title: Re: Get Display Scale
Post by: kdmoyers on March 06, 2025, 10:36:55 AM
(( Clearly windows knows the situation, because as you drag the window from one monitor to the other, as it gets to halfway across, it resizes automatically.  Pretty cool windows feature. ))
Title: Re: Get Display Scale
Post by: spl on March 06, 2025, 12:22:10 PM
Quote from: kdmoyers on March 06, 2025, 10:36:55 AM(( Clearly windows knows the situation, because as you drag the window from one monitor to the other, as it gets to halfway across, it resizes automatically.  Pretty cool windows feature. ))

Just curious; I no longer have multiple monitors, but with more than 1 would running a WMI query against

win32_videocontroller class and parsing caption, CurrentHorizontalResolution, CurrentVerticalResolution

get useful results? Something like
decimals(1)
strComputer = "."
oWMI = GetObject( "winmgmts:\\" : strComputer : "\root\cimv2")
WQL = "SELECT * FROM Win32_videocontroller" 
Monitors= oWMI.ExecQuery(WQL)
ForEach m in Monitors
      caption =  m.caption
      h = m.CurrentHorizontalResolution * 1.0
      v = m.CurrentVerticalResolution * 1.0
      scale = (h/v)*100:"%%"
      Display(5,"Monitor",caption:@LF:h:@LF:v:@LF:scale)
Next   
Monitors=0
oWMI=0
Exit
Title: Re: Get Display Scale
Post by: kdmoyers on March 06, 2025, 12:58:12 PM
No dice.  the Geep says "WMI's Win32_VideoController class focuses on the video adapter (e.g., resolution, refresh rate) and does not expose the per-monitor DPI scaling settings. Display scaling (the DPI setting) is managed at the operating system level and isn't available via that WMI class. Instead, you need to use native Windows API functions—such as GetDpiForMonitor from Shcore.dll—to retrieve the effective DPI and calculate the scaling (where 96 DPI equals 100% scaling)."

I tried a powershell script that enumerated the monitors, but it said they were both 100%.

I'm tossing in the towel. Like a lot of these screwy rabbit holes, I don't *really* care, I was just curious.  After all, if we can't take a minute to look under rocks, what are we here for?
Title: Re: Get Display Scale
Post by: JTaylor on March 06, 2025, 01:01:06 PM
So the goal is to get scaling for non-primary monitor?
Title: Re: Get Display Scale
Post by: td on March 06, 2025, 01:12:34 PM
Quote from: kdmoyers on March 06, 2025, 12:58:12 PMNo dice.  the Geep says "WMI's Win32_VideoController class focuses on the video adapter (e.g., resolution, refresh rate) and does not expose the per-monitor DPI scaling settings. Display scaling (the DPI setting) is managed at the operating system level and isn't available via that WMI class. Instead, you need to use native Windows API functions—such as GetDpiForMonitor from Shcore.dll—to retrieve the effective DPI and calculate the scaling (where 96 DPI equals 100% scaling)."

I tried a powershell script that enumerated the monitors, but it said they were both 100%.

I'm tossing in the towel. Like a lot of these screwy rabbit holes, I don't *really* care, I was just curious.  After all, if we can't take a minute to look under rocks, what are we here for?

Thanks for checking it out. The obvious solution would be to set WinBatch manifests multi-monitor DPI aware. The downside is that it took a lot of work to make WinBatch DPI aware and would take a lot more to make all the graphical elements work in that environment. This is particularly true when, as you pointed out, the OS can do it for you in many cases.
Title: Re: Get Display Scale
Post by: spl on March 07, 2025, 04:07:00 AM
Quote from: kdmoyers on March 06, 2025, 12:58:12 PMI tried a powershell script that enumerated the monitors, but it said they were both 100%.


Yeah. I tried this on win11. Scale is 100% while resolution came out 177.78% [both recommended].
$module = "DisplaySettings"
if (!(Get-Module -ListAvailable -Name $module)) {Install-Module -Name $module}
Import-Module -Name $module
$res = Get-DisplayResolution
$scale = ($res.dmpelswidth/$res.dmPelsHeight).tostring("P")
$scale
Title: Re: Get Display Scale
Post by: td on March 07, 2025, 08:55:20 AM
Scaling is not the same as resolution. Scaling is, in effect, changing the number of pixels per inch by changing the size of an inch. That is one reason why the Windows API documentation uses the term "logical" pixels.
Title: Re: Get Display Scale
Post by: spl on March 07, 2025, 11:36:20 AM
Quote from: td on March 07, 2025, 08:55:20 AMScaling is not the same as resolution.

Of course. But I'm with Kirby that I don't really care or need to know but it is fun digging into the issue. Below PS executes C# code, suspect it normally returns 100 and don't see how it would iterate multiple monitors.
Add-Type @'
  using System;
  using System.Runtime.InteropServices;
  using System.Drawing;

  public class DPI {
    [DllImport("gdi32.dll")]
    static extern int GetDeviceCaps(IntPtr hdc, int nIndex);

    public enum DeviceCap {
      VERTRES = 10,
      DESKTOPVERTRES = 117
    }

    public static float scaling() {
      Graphics g = Graphics.FromHwnd(IntPtr.Zero);
      IntPtr desktop = g.GetHdc();
      int LogicalScreenHeight = GetDeviceCaps(desktop, (int)DeviceCap.VERTRES);
      int PhysicalScreenHeight = GetDeviceCaps(desktop, (int)DeviceCap.DESKTOPVERTRES);

      return (float)PhysicalScreenHeight / (float)LogicalScreenHeight;
    }
  }
'@ -ReferencedAssemblies 'System.Drawing.dll'

[Math]::round([DPI]::scaling(), 2) * 100
Title: Re: Get Display Scale
Post by: td on March 07, 2025, 01:04:39 PM
Ignorance is bliss?
Title: Re: Get Display Scale
Post by: spl on March 07, 2025, 01:20:28 PM
Quote from: td on March 07, 2025, 01:04:39 PMIgnorance is bliss?

Nah.. more like forewarned is forearmed or putting tariffs on Canadian Geese.
Title: Re: Get Display Scale
Post by: MrLeadFoot on March 11, 2025, 11:44:43 AM
Quote from: td on March 06, 2025, 07:41:17 AMThis topic has been covered before
https://forum.winbatch.com/index.php?msg=16302 (https://forum.winbatch.com/index.php?msg=16302)

A repost of the example in the link:

;; Note: If WinBatch was not manifested to be DPI aware
;; this would not work.  But it is so it does.

hDC = DllCall("User32.dll", long_ptr:"GetDC", lpnull)

LOGPIXELSX = 88
nPixPerInchX = DllCall("Gdi32.dll", long:"GetDeviceCaps", long:hDC, long:LOGPIXELSX)

;; 96 is the default Windows scaling.
nScallingPercent = (nPixPerInchX/96.0) * 100
x =  DllCall("User32.dll", long:"ReleaseDC", lpnull, long:hDC)
Message("Desktop Scalling",  nScallingPercent:"%%")

;; This would require Windows 10 - 1604 or later
;;nPixPerInchX10 = DllCall("User32.dll", long:"GetDpiForWindow", long:DllHwnd(""))

exit


Thank you. May I ask, what version of WinBatch was this written for?
Title: Re: Get Display Scale
Post by: td on March 11, 2025, 01:22:14 PM
It should work on just about any version released over the last 10 to 15 years. The only thing that is newish is the long_ptr parameter type. If it is an issue, change the type to long.
Title: Re: Get Display Scale
Post by: MrLeadFoot on March 11, 2025, 04:06:27 PM
Quote from: td on March 11, 2025, 01:22:14 PMIt should work on just about any version released over the last 10 to 15 years. The only thing that is newish is the long_ptr parameter type. If it is an issue, change the type to long.
Thank you, again.

I inadvertently left this line out, and it still works. What does this line do?
x =  DllCall("User32.dll", long:"ReleaseDC", lpnull, long:hDC)
Title: Re: Get Display Scale
Post by: JTaylor on March 11, 2025, 08:06:57 PM
It is releasing the Device Context acquired by this line

   hDC = DllCall("User32.dll", long_ptr:"GetDC", lpnull)

Jim