Rad Studio 12.1中FireMonkey项目Screen->Displays返回错误屏幕尺寸
问题原因与解决方案
核心原因:FireMonkey高DPI处理逻辑变更
从RAD Studio 10.3到12.1,FireMonkey的高DPI缩放感知与像素值返回逻辑发生了调整。你的主屏幕(3840×2400)是高DPI屏幕(缩放比例200%),12.1中默认情况下Screen->Displays返回的是缩放后逻辑像素,而非物理像素——1536×960正是3840×2400除以2的结果。同时,屏幕位置偏移问题也源于DPI缩放后的坐标映射错误。
10.3版本中FireMonkey对高DPI的处理更依赖系统全局缩放,直接返回物理像素;而12.1默认启用了每监视器DPI感知(Per-Monitor DPI Awareness v2),但FireMonkey的Screen组件在某些场景下未正确转换物理像素与逻辑像素的对应关系,导致尺寸和位置偏差。
解决步骤
1. 调整项目DPI设置
打开RAD Studio 12.1的项目选项:
- 导航到Application > Manifest,确保Per-Monitor DPI Awareness设置为
PerMonitorV2(或根据需求调整)。 - 切换到Application > Appearance,确认Scaling选项中的High DPI Mode设置为
System或Per Monitor,避免强制缩放逻辑像素。
2. 手动转换物理像素与逻辑像素
如果项目需要保留当前DPI设置,可通过FireMonkey的DPI转换方法获取物理像素:
Screen->UpdateDisplayInformation(); for (int i = 0; i < Screen->DisplayCount; i++) { // 获取当前屏幕的DPI缩放比例 float dpiScale = Screen->Displays[i].DPIScaling; // 转换为物理像素 int physicalWidth = static_cast<int>(Screen->Displays[i].Bounds.Width() * dpiScale); int physicalHeight = static_cast<int>(Screen->Displays[i].Bounds.Height() * dpiScale); // 获取物理位置(Left/Top同样需乘以dpiScale) int physicalLeft = static_cast<int>(Screen->Displays[i].Bounds.Left * dpiScale); MainForm->LogMsg("Screen : " + IntToStr(i + 1) + ", 物理尺寸: " + IntToStr(physicalWidth) + "×" + IntToStr(physicalHeight) + ", 物理位置Left: " + IntToStr(physicalLeft)); }
3. 绕过FireMonkey封装,调用Windows原生API
如果上述方法仍无法解决,直接使用Windows原生API获取准确的屏幕信息,不受FireMonkey逻辑影响:
#include <Windows.h> // 回调函数用于枚举显示器 BOOL CALLBACK EnumMonitorsProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { std::vector<RECT>* pMonitorRects = reinterpret_cast<std::vector<RECT>*>(dwData); pMonitorRects->push_back(*lprcMonitor); return TRUE; } // 获取所有物理屏幕的尺寸与位置 void GetPhysicalDisplays(std::vector<RECT>& outMonitorRects) { outMonitorRects.clear(); EnumDisplayMonitors(NULL, NULL, EnumMonitorsProc, reinterpret_cast<LPARAM>(&outMonitorRects)); } // 使用示例 std::vector<RECT> monitorRects; GetPhysicalDisplays(monitorRects); for (size_t i = 0; i < monitorRects.size(); i++) { RECT rc = monitorRects[i]; int width = rc.right - rc.left; int height = rc.bottom - rc.top; MainForm->LogMsg("Screen : " + IntToStr(i + 1) + "," + IntToStr(width) + "," + IntToStr(height) + ", Left: " + IntToStr(rc.left)); }
4. 验证屏幕索引顺序
12.1中Screen->Displays的索引顺序可能与10.3不同,建议同时打印屏幕的DeviceName或Primary属性,确认索引对应的实际屏幕:
MainForm->LogMsg("Screen : " + IntToStr(i + 1) + ", IsPrimary: " + BoolToStr(Screen->Displays[i].Primary) + ", DeviceName: " + Screen->Displays[i].DeviceName);
内容的提问来源于stack exchange,提问作者Barry Andrews
相关产品推荐
相关产品推荐

