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
相关产品推荐
相关产品推荐

