GetClientRect返回缩放后的尺寸?

5
我正在使用C#通过PInvoke来调用GetClientRect方法,以获取一个WindowsForm面板的(作为DirectX渲染目标区域)尺寸。我原本以为WinAPI会提供未缩放的尺寸(即返回值应该与Windows显示DPI设置无关),但实际上它似乎给出的是已经缩放过的尺寸(至少在Windows 8.1上是如此,我还没有在其他操作系统上进行测试)。 ClientRectangle属性也返回与GetClientRect相同的已缩放尺寸。当我将表单的AutoScaleMode更改为none时,这种情况仍然存在。
这是GetClientRect的预期行为吗?如果是,我该如何获取未缩放的尺寸?
编辑:这只影响Windows 8.1。在Windows 7上测试了一下,GetClientRect返回的是未缩放的尺寸!

实际的DPI值非常重要。您必须声明您的应用程序dpiAware,以禁用超过125%的DPI虚拟化功能。 - Hans Passant
我的Win8.1 DPI设置为125%(中等/默认)。所以DPI虚拟化不应该启动了吗?无论如何,请参考Cody的答案。显然,Win8.1也会对125%进行DPI虚拟化。 - Zach Saw
1个回答

3

使用P/Invoke调用GetClientRect没有任何优势。通过查询ClientRectangle属性,您可以得到完全相同的结果。

是的,这两个方法都将返回以物理像素为单位的客户区域大小,显然这不是您想要的。

您可能正在寻找ID2D1RenderTarget::GetSize方法,它返回设备独立像素(DIPs)中呈现目标的大小。

如果必须转换物理像素(例如来自鼠标事件),则可以使用类似ID2D1Factory::GetDesktopDpi的方法来获取当前缩放因子,然后使用这些因子将物理像素转换为DIPs。

你在Windows 8.1中看到不同的行为可能是处理DPI缩放方式的更改的结果。你可能会发现,指示应用程序具有DPI感知能力比上述方法更容易。这将阻止UI自动缩放以匹配当前的DPI设置。你可以通过将以下内容添加到应用程序清单来实现此操作:
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" >
    <asmv3:application>
        <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
            <dpiAware>True/PM</dpiAware>
        </asmv3:windowsSettings>
    </asmv3:application>
</assembly>

从 Visual Studio 中,右键单击项目并点击“添加” -> “新建项”。选择“应用程序清单文件”,然后添加上述代码。
相关阅读:编写支持 DPI 的桌面和 Win32 应用程序

增加我的困惑的是,Win8.1会在我全屏独占模式切换失败第一次后,在下次启动我的应用程序时打开DPI感知。这使得很难重现该问题,因为它仅会在系统重新启动后第一次启动应用程序时出现。 - Zach Saw
是的,这很奇怪。我个人没有使用Windows 8.1,但我不认为操作系统可以任意地声明一个应用程序支持DPI。据我所知,作为程序员,您必须自己完成这项工作。如果应用程序没有指示它是DPI感知的,则它就不是。您将获得兼容性行为。 - Cody Gray
这是预期的行为,但不幸的是情况并非如此。我可以经验性地证明,在全屏模式切换失败后,应用程序第二次运行时,DPI虚拟化会被禁用,直到下一次重启(或者您重命名exe文件)。 - Zach Saw
有趣。唯一有意义的是它检测到你调用了某个API,并根据此将你的应用程序视为DPI感知。尽管仍然很奇怪。如果我有Windows 8.1,我会要求重现代码。如果你仍然关心,可以提一个新问题询问。 - Cody Gray
是的,一个合理的猜测是我让D3D以缩放的尺寸全屏显示。Win8.1只需要检查这些缩放尺寸是否能够在全屏独占模式失败时转换回有效的全屏分辨率。另一个合理的猜测是这是Win兼容性服务所做的。我需要在有时间时进一步调查这个问题。 - Zach Saw

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接