可以在每个会话的基础上以编程方式启用/禁用 DPI 缩放吗?

Posted

技术标签:

【中文标题】可以在每个会话的基础上以编程方式启用/禁用 DPI 缩放吗?【英文标题】:Can DPI scaling be enabled/disabled programmatically on a per-session basis? 【发布时间】:2017-11-07 22:43:10 【问题描述】:

我的应用程序恰好是使用 Python 编写的,它使用封装了 SDL 的 pygame,但我想这可能是一个与 Windows API 相关的更普遍的问题。

在我的一些 Python 应用程序中,即使在高分辨率下,我也希望在 Windows 10 下进行逐像素控制。例如,我希望能够确保如果我的 Surface Pro 3 具有 2160x1440 的原始分辨率,那么我可以进入具有这些尺寸的全屏模式并呈现完全具有这些尺寸的全屏图像。

这样做的障碍是“DPI 缩放”。默认情况下,在 Windows 的设置 -> 显示下,“更改文本、应用程序和其他项目的大小”的值为“150%(推荐)”,结果我只能看到我的图像的 2/3。我发现了如何解决这种行为...

    在系统范围内,通过将该滑块向下移动到 100%(但对于大多数其他应用程序来说,这是 undesirable) 仅适用于python.exepythonw.exe,通过转到这些可执行文件的“属性”对话框、“兼容性”选项卡,然后单击“在高 DPI 设置下禁用显示缩放”。我可以为我一个人或为所有用户执行此操作。我还可以通过以编程方式在注册表中设置适当的键来自动执行此过程。或通过.exe.manifest 文件(这似乎也需要更改全局设置,以偏爱外部清单,可能会对其他应用程序产生副作用)。

我的问题是:在我打开图形窗口之前,我可以在每次启动时从我的程序内部执行此操作吗?我或任何使用我的软件的人都不一定希望为所有 Python 应用程序启用此设置——我们可能只是在运行特定的Python 程序时才需要它。我想可能有一个 winapi 调用(或者在 SDL 中失败,由 pygame 包装)可以实现这一点,但到目前为止我的研究是空白。

【问题讨论】:

SetProcessDpiAwareness. 如果您需要支持 Windows 7,请阅读@IInspectable 链接中提到的向您的 EXE 添加“清单”资源 Windows 7 有一个单独的 API 调用,SetProcessDPIAware()。如果您无法完全控制清单文件,则可能需要这样做(因为每个可执行文件只能有一个)。 仅供参考 150% 并不总是默认值,Windows 根据屏幕尺寸和 EDID 数据选择默认值。 看看this answer——它在 Windows 10 上对我有用! 【参考方案1】:

这是我正在寻找的答案,基于 IInspectable 和 andlabs 的 cmets(非常感谢):

  import ctypes

  # Query DPI Awareness (Windows 10 and 8)
  awareness = ctypes.c_int()
  errorCode = ctypes.windll.shcore.GetProcessDpiAwareness(0, ctypes.byref(awareness))
  print(awareness.value)

  # Set DPI Awareness  (Windows 10 and 8)
  errorCode = ctypes.windll.shcore.SetProcessDpiAwareness(2)
  # the argument is the awareness level, which can be 0, 1 or 2:
  # for 1-to-1 pixel control I seem to need it to be non-zero (I'm using level 2)

  # Set DPI Awareness  (Windows 7 and Vista)
  success = ctypes.windll.user32.SetProcessDPIAware()
  # behaviour on later OSes is undefined, although when I run it on my Windows 10 machine, it seems to work with effects identical to SetProcessDpiAwareness(1)

are defined的认知度如下:

typedef enum _PROCESS_DPI_AWARENESS  
    PROCESS_DPI_UNAWARE = 0,
    /*  DPI unaware. This app 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. */

    PROCESS_SYSTEM_DPI_AWARE = 1,
    /*  System DPI aware. This app does not scale for DPI changes.
        It will query for the DPI once and use that value for the
        lifetime of the app. If the DPI changes, the app 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. */

    PROCESS_PER_MONITOR_DPI_AWARE = 2
    /*  Per monitor DPI aware. This app checks for the DPI when it is
        created and adjusts the scale factor whenever the DPI changes.
        These applications are not automatically scaled by the system. */
 PROCESS_DPI_AWARENESS;

2 级听起来最适合我的目标,但只要系统分辨率/DPI 缩放没有变化,1 级也可以。

SetProcessDpiAwareness 将失败并返回 errorCode = -2147024891 = 0x80070005 = E_ACCESSDENIED,如果它先前已被当前进程调用(包括在进程启动时由系统调用,由于注册表项或 .manifest 文件)

【讨论】:

为了使它成为一个好的、独立的答案,我将解释为什么您选择“幻数”作为SetProcessDpiAwareness() 的参数,甚至可以为代码中的可能值定义命名常量样本。 @StefanRickli 感谢您提出的编辑建议。 byref 也适合您吗?这就是我根据这个答案在最新版本的代码中找到的。

以上是关于可以在每个会话的基础上以编程方式启用/禁用 DPI 缩放吗?的主要内容,如果未能解决你的问题,请参考以下文章

如何在android studio上以编程方式检查自动启动权限是启用还是禁用

如何在Android 6.x上以编程方式启用或禁用GPS?

在 UITextView 上以编程方式禁用 iOS8 Quicktype 键盘

在 OS X 上以编程方式启用“全键盘访问”

iOS 在 UIWebBrowserView 上以编程方式禁用 Quicktype 键盘

以编程方式禁用鼠标和键盘