调用GetWindowRect获取截图时返回坐标存在较大偏差问题
解决GetWindowRect返回窗口坐标偏差的问题
嘿,这个问题我之前调试截图工具时也踩过坑!大概率是高DPI显示器的缩放适配没做好,给你一步步拆解原因和解决方案:
最常见的元凶:未适配高DPI缩放
现在主流显示器都是高分辨率,系统默认会开125%/150%这类DPI缩放。如果你的程序没声明「DPI感知」,Windows会自动给窗口做“虚拟缩放”,这时候GetWindowRect返回的是缩放后的虚拟坐标,和实际屏幕像素坐标不匹配,偏差自然就出来了。
具体解决步骤
1. 给程序加上DPI感知(最关键一步)
有两种简单的方式:
方式一:修改应用清单(推荐)
找到项目里的app.manifest文件(如果没有就添加一个),找到下面这段,取消注释并改成true/PM:<application xmlns="urn:schemas-microsoft-com:asm.v3"> <windowsSettings> <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware> </windowsSettings> </application>true/PM表示支持「每显示器DPI感知」,能适配不同屏幕的缩放比例,是目前Windows推荐的模式。方式二:代码动态设置(适合无法改清单的场景)
在程序启动最开始(比如Main方法或者窗体Load事件的最开头)调用这个API:Private Declare Function SetProcessDpiAwarenessContext Lib "user32" (ByVal dpiContext As Integer) As Integer ' 调用这个设置DPI感知 SetProcessDpiAwarenessContext(&H80000002) ' 对应DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
2. 手动坐标换算(如果还存在偏差)
要是已经开了DPI感知但还是不对,可以手动用窗口的实际DPI来换算坐标:
Private Declare Function GetDpiForWindow Lib "user32" (ByVal hwnd As IntPtr) As Integer ' 获取窗口句柄 Dim targetHwnd As IntPtr = GetActiveWindow() ' 注意:这个是当前进程的活动窗口,要是截外部窗口得换GetForegroundWindow() Dim windowRect As RECT GetWindowRect(targetHwnd, windowRect) ' 获取窗口对应的DPI,换算缩放比例 Dim dpi As Integer = GetDpiForWindow(targetHwnd) Dim scaleRatio As Double = dpi / 96.0 ' 96是系统默认DPI ' 转换为真实像素坐标 Dim realLeft = CInt(windowRect.Left * scaleRatio) Dim realTop = CInt(windowRect.Top * scaleRatio) Dim realRight = CInt(windowRect.Right * scaleRatio) Dim realBottom = CInt(windowRect.Bottom * scaleRatio)
3. 检查窗口句柄是否正确
你用的GetActiveWindow()只会返回当前进程内的活动窗口,如果你的目标是截图其他程序的窗口,这个句柄就错了!这种情况建议用GetForegroundWindow()(获取系统当前前台的窗口),或者用FindWindow根据窗口标题查找目标窗口。
最后测试
把DPI感知设置好后,再跑你的代码,应该就能拿到和实际屏幕像素匹配的坐标了。截图的时候记得用转换后的宽高(realRight - realLeft和realBottom - realTop)来创建截图区域,这样截出来的图就不会错位啦。
内容的提问来源于stack exchange,提问作者Neil
相关产品推荐
相关产品推荐

