You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Windows缩放比例下屏幕截图区域偏移问题求助

解决Windows DPI缩放时截图偏移的问题

我之前也碰到过一模一样的问题!Windows的DPI缩放确实会给GDI类的截图操作添不少麻烦,核心问题在于逻辑像素和物理像素不匹配,加上默认WinForms应用没开启DPI感知,导致你传入CopyFromScreen的坐标是逻辑层面的,但这个方法实际需要的是屏幕物理像素坐标,自然就出现偏移了。

下面是我亲测有效的解决方案,分步骤来:

1. 给应用开启DPI感知(最关键的一步)

默认新建的WinForms应用是不感知DPI的,这会导致系统自动缩放整个应用窗口,同时所有坐标计算都用逻辑像素。我们需要修改app.manifest文件让它支持DPI感知:

找到项目里的app.manifest(没有的话就添加一个),取消注释下面这段配置:

<application xmlns="urn:schemas-microsoft-com:asm.v3">
  <windowsSettings>
    <!-- 启用每显示器DPI感知,适配多显示器不同缩放的场景 -->
    <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
  </windowsSettings>
</application>

如果你的项目比较旧,也可以用旧版的dpiAware设置:

<asmv3:application>
  <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
    <dpiAware>true</dpiAware>
  </asmv3:windowsSettings>
</asmv3:application>

改完后重新编译项目,这一步是基础,不做的话后面的坐标转换可能还是有问题。

2. 计算当前的DPI缩放因子

开启DPI感知后,我们可以准确获取当前窗口/屏幕的缩放比例:

方法1:用Graphics对象获取(WinForms常用)

using (Graphics g = this.CreateGraphics())
{
    // 96是默认100%缩放的DPI值
    float scaleX = g.DpiX / 96f;
    float scaleY = g.DpiY / 96f;
}

方法2:用Win32 API获取(更精准,适合多显示器)

如果你的应用需要支持多显示器不同缩放,用GetDpiForWindow API更准确:

using System.Runtime.InteropServices;

[DllImport("user32.dll")]
private static extern int GetDpiForWindow(IntPtr hwnd);

// 在你的窗口类中调用
int currentDpi = GetDpiForWindow(this.Handle);
float scaleFactor = currentDpi / 96f;

3. 修改CopyFromScreen的坐标和尺寸

现在你需要把所有传入CopyFromScreen逻辑坐标、宽度、高度都转换成物理像素:
假设你之前的代码是这样的(用逻辑坐标):

// 比如从用户选择的区域获取的逻辑坐标和尺寸
int sourceX = (int)selectionRect.X;
int sourceY = (int)selectionRect.Y;
int captureWidth = (int)selectionRect.Width;
int captureHeight = (int)selectionRect.Height;

using (Graphics g = Graphics.FromImage(captureImage))
{
    g.CopyFromScreen(sourceX, sourceY, 0, 0, new Size(captureWidth, captureHeight));
}

修改后变成:

// 先获取缩放因子
float scaleX;
float scaleY;
using (Graphics g = this.CreateGraphics())
{
    scaleX = g.DpiX / 96f;
    scaleY = g.DpiY / 96f;
}

// 转换为物理像素
int physicalX = (int)(sourceX * scaleX);
int physicalY = (int)(sourceY * scaleY);
int physicalWidth = (int)(captureWidth * scaleX);
int physicalHeight = (int)(captureHeight * scaleY);

using (Graphics g = Graphics.FromImage(captureImage))
{
    g.CopyFromScreen(physicalX, physicalY, 0, 0, new Size(physicalWidth, physicalHeight));
}

额外注意点

  • 如果是用Cursor.Position获取鼠标坐标,开启DPI感知后它返回的就是物理像素,不需要再转换;如果没开启的话,它返回的是逻辑像素,需要乘缩放因子。
  • 测试的时候一定要覆盖不同缩放比例(125%、150%、200%),还有多显示器不同缩放的场景,确保所有情况都正常。

按照这个流程改完,应该就能解决截图偏移的问题了,我之前就是这么搞定的!

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

火山引擎 最新活动