【问题标题】:Disable high DPI scaling禁用高 DPI 缩放
【发布时间】:2021-12-15 13:16:41
【问题描述】:

启用屏幕缩放后,我想从我的代码中获取正确的数据。所以我使用SetProcessDPIAware()函数:

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

输出:

{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

与c#代码相同的情况:

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);
    }
}

那么这是Screen.PrimaryScreen.Bounds 的错误还是我必须做其他事情才能从该属性中获取正确的数据?

如果唯一的方法是使用SystemInformation 类,那么我如何获取关于第二台监视器的数据,而不仅仅是关于主监视器的数据?

有没有办法为 powershell.exe / powershell_ise.exe 设置this 设置?

【问题讨论】:

  • 如果您需要每台显示器的 DPI 感知,将 DPI 感知设置为 DPI_AWARENESS_CONTEXT_SYSTEM_AWARE 不会解决问题。

标签: c# .net powershell winapi


【解决方案1】:

这是我使用并为我工作的:

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

然后你可以调用这个类并获取 DPI 如下:

$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

最后,始终使用相对大小,例如:

$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)
})

以此类推,其中X 可以是intdouble


  • 左显示器:1920x1080 - 175% 缩放
  • 右显示器:1920x1080 - 100% 缩放

【讨论】:

  • 如果您为不同的显示设备设置不同的 DPI 设置,它的效果如何?
  • @IInspectable 我真的不确定,我所知道的是该类将返回加载该类的显示设备的 DPI 值,我使用PrimaryScreen.WorkingArea 设置@ 987654333@也是因为这个。至于在使用 2 种不同的 DPI 设置时它是如何工作的,我认为 Windows 正在处理这个问题,但我可能错了(请参阅我的 gif 编辑)
  • “加载类的显示设备的 DPI 值” - 这没有任何意义。类型未在显示设备上激活。 “当使用 2 种不同的 DPI 设置时,我认为 Windows 正在处理这个问题” - 这应该如何工作?这不像对GetDeviceCaps 的调用会神奇地返回不同的值。它返回一个值,并且由于类不在显示设备上,所以就是这样。我怀疑这个提议的答案有什么用处。
  • @IInspectable 你是对的。该类始终返回主显示设备的 DPI。我发布这个是因为它对我个人有用,我从未声称自己是专家。如果您有可行的解决方案,请将其作为答案发布。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-05-18
  • 1970-01-01
  • 2018-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多