You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows 11自定义缩放场景下TForm尺寸存储恢复问题解决

问题描述

Windows 11新增了自定义屏幕缩放选项。在Delphi应用中,每次关闭窗体时会将TForm的Width、Height等参数存储到INI文件,下次打开时恢复。但开启自定义缩放后,存储的尺寸值会被缩放比例放大,而设置时会正确应用缩放,导致窗体每次打开都变大。请问是否需要手动除以缩放因子来补偿?同时求通过Windows API获取自定义缩放设置的方法。

原存储与恢复代码如下:

// 存储到文件代码
IniFile.WriteInteger('FORM','Width',AForm.Width);
IniFile.WriteInteger('FORM','Top',AForm.Top);

// 从文件恢复代码
StoredHeight:= IniFile.readInteger('FORM','Height',AForm.Height);
StoredWidth := IniFile.readInteger('FORM','Width',AForm.Width);

if StoredWidth >= Screen.Width then
  AForm.Width := Screen.Width
else
  begin
    AForm.Width := StoredWidth;
    if StoredLeft < 0 then
    AForm.left  := 0
    else
      AForm.left := StoredLeft;
  end;
if StoredHeight >= Screen.Height then
  AForm.Height := Screen.Height
else
  begin
    AForm.Height :=  StoredHeight;
    if StoredTop < 0 then
    AForm.Top := 0
    else
      AForm.Top := StoredTop;
  end;
解决方案

1. 是否需要手动补偿缩放?

是的,必须手动处理缩放因子。因为Delphi的TForm的Width/Height/Top/Left属性返回的是经过系统缩放后的设备像素值,而我们需要存储的是窗体的逻辑像素值(不受缩放影响的原始尺寸)。如果直接存储设备像素,下次在相同缩放比例下恢复时,系统会再次对该值应用缩放,导致窗体尺寸被重复放大。

正确逻辑:

  • 存储时:将设备像素值除以缩放因子,得到逻辑像素值后写入INI
  • 恢复时:从INI读取逻辑像素值,乘以缩放因子,得到设备像素值后设置给窗体

2. 通过Windows API获取缩放因子的方法

可以使用GetDpiForWindow或GetDpiForMonitorAPI获取对应窗口/显示器的缩放DPI,再计算缩放因子(缩放因子 = DPI / 96,系统默认DPI为96)。

兼容Windows 10及以上的缩放因子获取代码

uses
  Winapi.Windows;

function GetFormScaleFactor(AForm: TForm): Double;
var
  Dpi: UINT;
begin
  // 获取窗体所在显示器的DPI
  Dpi := GetDpiForWindow(AForm.Handle);
  // 计算缩放因子
  Result := Dpi / 96.0;
end;

兼容旧版Windows的缩放因子获取代码

uses
  Winapi.Windows;

function GetFormScaleFactor(AForm: TForm): Double;
var
  hMonitor: HMONITOR;
  hDC: HDC;
  DpiX: Integer;
begin
  hMonitor := MonitorFromWindow(AForm.Handle, MONITOR_DEFAULTTONEAREST);
  hDC := CreateDC(nil, nil, nil, nil);
  try
    DpiX := GetDeviceCaps(hDC, LOGPIXELSX);
    Result := DpiX / 96.0;
  finally
    DeleteDC(hDC);
  end;
end;

3. 修改后的存储与恢复代码

存储逻辑

var
  ScaleFactor: Double;
begin
  ScaleFactor := GetFormScaleFactor(AForm);
  // 转换为逻辑像素后存储
  IniFile.WriteInteger('FORM','Width', Round(AForm.Width / ScaleFactor));
  IniFile.WriteInteger('FORM','Height', Round(AForm.Height / ScaleFactor));
  IniFile.WriteInteger('FORM','Top', Round(AForm.Top / ScaleFactor));
  IniFile.WriteInteger('FORM','Left', Round(AForm.Left / ScaleFactor));
end;

恢复逻辑

var
  ScaleFactor: Double;
  StoredWidth, StoredHeight, StoredTop, StoredLeft: Integer;
begin
  ScaleFactor := GetFormScaleFactor(AForm);
  
  // 读取逻辑像素值
  StoredWidth := IniFile.ReadInteger('FORM','Width', Round(AForm.Width / ScaleFactor));
  StoredHeight := IniFile.ReadInteger('FORM','Height', Round(AForm.Height / ScaleFactor));
  StoredTop := IniFile.ReadInteger('FORM','Top', Round(AForm.Top / ScaleFactor));
  StoredLeft := IniFile.ReadInteger('FORM','Left', Round(AForm.Left / ScaleFactor));

  // 转换为设备像素后设置
  if StoredWidth >= Round(Screen.Width / ScaleFactor) then
    AForm.Width := Screen.Width
  else
  begin
    AForm.Width := Round(StoredWidth * ScaleFactor);
    if StoredLeft < 0 then
      AForm.Left := 0
    else
      AForm.Left := Round(StoredLeft * ScaleFactor);
  end;

  if StoredHeight >= Round(Screen.Height / ScaleFactor) then
    AForm.Height := Screen.Height
  else
  begin
    AForm.Height := Round(StoredHeight * ScaleFactor);
    if StoredTop < 0 then
      AForm.Top := 0
    else
      AForm.Top := Round(StoredTop * ScaleFactor);
  end;
end;

内容的提问来源于stack exchange,提问作者UnDiUdin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 22:05:26