I want to get correct data from my code when screen scaling is enabled. So i'm using SetProcessDPIAware() function:
using namespace System.Windows.Forms
Clear-Host
Add-Type -AssemblyName System.Windows.Forms
Add-Type -Name User32 -Namespace W32 '
[DllImport("user32.dll")]
public static extern bool SetProcessDPIAware();'
[Cursor]::Position | Write-Host
[Screen]::PrimaryScreen.Bounds.Size | Write-Host
[SystemInformation]::PrimaryMonitorSize | Write-Host
[W32.User32]::SetProcessDPIAware()
[Cursor]::Position | Write-Host
[Screen]::PrimaryScreen.Bounds.Size | Write-Host
[SystemInformation]::PrimaryMonitorSize | Write-Host
Output:
{X=630,Y=313}             # ❌ incorrect data
{Width=2048, Height=864}  # ❌ incorrect data
{Width=2048, Height=864}  # ❌ incorrect data
True
{X=788,Y=391}             # ✔️ data became correct
{Width=2048, Height=864}  # ❌ still incorrect data
{Width=2560, Height=1080} # ✔️ data became correct
The same situation with c# code:
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
 
public class Class1
{
    [DllImport("user32.dll")]
    static extern bool SetProcessDPIAware();
    public static void Main()
    {
        Console.WriteLine("\n");
        Console.WriteLine(Cursor.Position);
        Console.WriteLine(Screen.PrimaryScreen.Bounds.Size);
        Console.WriteLine(SystemInformation.PrimaryMonitorSize);
        Console.WriteLine(SetProcessDPIAware());
        Console.WriteLine(Cursor.Position);
        Console.WriteLine(Screen.PrimaryScreen.Bounds.Size);
        Console.WriteLine(SystemInformation.PrimaryMonitorSize);
    }
}
So is this a bug of Screen.PrimaryScreen.Bounds or i have to do something else to get correct data from this property?
And if the only way is to use the SystemInformation class, then how do I get data about the second monitor, not only about the Primary one?
OR Is there a way to set this setting for powershell.exe / powershell_ise.exe ?
This is what I use and works for me:
Add-Type -TypeDefinition @'
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 -IgnoreWarnings -WarningAction Ignore
Then you can call this class and get the DPI like:
$DPI = [math]::round([dpi]::scaling(), 2) * 100
$bounds = [System.Windows.Forms.Screen]::PrimaryScreen.WorkingArea
$bounds.Width = ($bounds.Width / 100) * $DPI
$bounds.Height = ($bounds.Height / 100) * $DPI
And lastly, always use relative sizes like:
$form.Size = [System.Drawing.Size]::new(($bounds.Width / X),($bounds.Height / X))
$form.Add_Resize({
    $txtBox.Size = [System.Drawing.Size]::new(($this.Width - X), XX)
})
And so on, where X can be an int or double.

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With