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

Magnification API截图比例不符及Delphi截图左右异常问题求助

修复Magnification API截图尺寸比例失调的问题

兄弟,你遇到的这个问题大概率是系统DPI缩放适配没做好导致的——现在很多高分辨率屏幕都会开DPI缩放(比如125%、150%),Magnification API默认是基于物理坐标工作的,但如果你的程序没做DPI感知,获取的桌面尺寸是逻辑尺寸,直接用逻辑尺寸去设置截图区域就会出现拉伸、截断的情况,还会莫名其妙出现滚动条。

下面给你一步步的修复方案,结合你的Delphi代码来调整:

1. 先让程序变成DPI感知的

这是基础中的基础,如果程序不声明自己支持高DPI,系统会自动缩放整个程序窗口,导致所有坐标都乱掉。

  • 在Delphi的项目选项里,找到「Application」→「Manifest」,勾选「Enable high DPI support」,或者手动给程序添加manifest文件,里面包含:
    <application xmlns="urn:schemas-microsoft-com:asm.v3">
      <windowsSettings>
        <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
      </windowsSettings>
    </application>
    

2. 获取正确的物理屏幕尺寸

之前你可能用的是GetSystemMetrics(SM_CXSCREEN)这类函数,拿到的是逻辑尺寸(已经被DPI缩放后的尺寸),我们需要获取物理像素尺寸:
首先添加必要的API声明:

type
  MONITOR_DPI_TYPE = (
    MDT_EFFECTIVE_DPI = 0,
    MDT_ANGULAR_DPI = 1,
    MDT_RAW_DPI = 2,
    MDT_DEFAULT = MDT_EFFECTIVE_DPI
  );

function GetDpiForMonitor(hmonitor: HMONITOR; dpiType: MONITOR_DPI_TYPE; out dpiX, dpiY: UINT): HRESULT; stdcall; external 'shcore.dll';
function GetSystemMetricsForDpi(nIndex: Integer; dpi: UINT): Integer; stdcall; external 'user32.dll';

然后在初始化时(比如FormCreate)计算缩放比例和物理尺寸:

var
  hMonitor: HMONITOR;
  dpiX, dpiY: UINT;
  scaleX, scaleY: Double;
  physicalScreenWidth, physicalScreenHeight: Integer;
begin
  // 获取主显示器句柄
  hMonitor := MonitorFromWindow(0, MONITOR_DEFAULTTOPRIMARY);
  // 获取当前显示器的有效DPI
  GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, dpiX, dpiY);
  // 计算缩放比例(96是系统默认100%缩放的DPI)
  scaleX := dpiX / 96.0;
  scaleY := dpiY / 96.0;
  // 获取物理屏幕的宽高(像素)
  physicalScreenWidth := Round(GetSystemMetricsForDpi(SM_CXSCREEN, dpiX) * scaleX);
  physicalScreenHeight := Round(GetSystemMetricsForDpi(SM_CYSCREEN, dpiY) * scaleY);
end;

3. 修正Magnification API的参数设置

创建放大镜窗口和设置源区域时,必须用物理尺寸,而不是逻辑尺寸:

// 创建放大镜窗口,尺寸设为物理屏幕大小
hMagWindow := CreateWindowW(MAG_WINDOW_CLASS, '', WS_POPUP or WS_VISIBLE,
  0, 0, physicalScreenWidth, physicalScreenHeight, 0, 0, hInstance, nil);
// 设置截图源为整个物理桌面
MagSetWindowSource(hMagWindow, Rect(0, 0, physicalScreenWidth, physicalScreenHeight));
// 设置1:1的缩放变换,避免图像拉伸
MagSetWindowTransform(hMagWindow, Matrix(scaleX, 0, 0, scaleY, 0, 0));

4. 截图时匹配物理尺寸

最后在捕获图像的时候,确保Bitmap的尺寸和物理屏幕一致:

procedure TForm1.CAPTUREClick(Sender: TObject);
var
  Bitmap: TBitmap;
  DC: HDC;
begin
  Bitmap := TBitmap.Create;
  try
    Bitmap.Width := physicalScreenWidth;
    Bitmap.Height := physicalScreenHeight;
    Bitmap.PixelFormat := pf32bit; // 用32位色避免颜色失真
    DC := GetDC(hMagWindow);
    try
      BitBlt(Bitmap.Canvas.Handle, 0, 0, physicalScreenWidth, physicalScreenHeight, DC, 0, 0, SRCCOPY);
    finally
      ReleaseDC(hMagWindow, DC);
    end;
    if SaveFileDialog1.Execute then
      Bitmap.SaveToFile(SaveFileDialog1.FileName);
  finally
    Bitmap.Free;
  end;
end;

额外注意点

如果是多显示器场景,你需要遍历所有显示器,获取每个显示器的物理尺寸和位置,然后拼接成一张完整的桌面截图——不过从你的问题描述看,应该是单显示器的情况,上面的代码足够解决问题。

按照这个思路改完之后,你再测试一下SO网站的桌面截图,应该就不会出现右侧截断、左侧拉伸和滚动条的问题了,尺寸比例会和实际桌面完全一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:32:07