【问题标题】:SetWindowPos() cross-process with multiple monitors and different display scalingsSetWindowPos() 跨进程与多个监视器和不同的显示缩放
【发布时间】:2019-07-12 01:38:22
【问题描述】:

我已经问过一个类似的问题here,但现在问题似乎有点不同,所以我想我应该为它创建一个新问题。

我正在使用SetWindowPos() 从另一个进程移动/调整窗口大小。只要所有屏幕都使用相同的显示缩放比例,它就可以正常工作,但在以下情况下,它不会按预期工作:

  • 主屏幕位于 (0,0),3440x1440 和 150% 缩放。
  • 辅助屏幕位于 (3440, 0),具有 900x1440 和 100% 缩放。
  • 我的应用程序是PROCESS_PER_MONITOR_DPI_AWARE_V2,目标应用程序是PROCESS_DPI_UNAWARE(由Windows 缩放)。

现在如果我移动一个窗口,使左上角在主屏幕上,而中心仍然在辅助屏幕上,例如到 (3400, 0)。

  SetWindowPos(hwnd, HWND_BOTTOM, 3300, 0, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE);

然后是这样的:

  • 窗口根据第二个屏幕的 100% 显示缩放比例进行缩放。
  • 窗口未移动到 (3300, 0)。相反,它在WM_WINDOWPOSCHANGING 消息中接收到的坐标是 (2200, 0)。坐标似乎已按比例缩小到逻辑坐标。

因此,我无法将窗口移动到该位置。我尝试在传递给SetWindowPos() 的坐标上使用PhysicalToLogicalPointForPerMonitorDPI(),但没有成功(它甚至不会更改坐标)。

现在似乎我无法将窗口移动到左上角位于主屏幕上的任何位置,但窗口的中心仍在辅助屏幕上,因为 Windows 会缩小坐标,如果我手动放大它们,然后我已经将窗口定位在第二个屏幕上,Windows 不再应用缩放。即使我能够解决这个问题,使用两屏设置手动计算缩放比例也是可能的,但是随着更多的屏幕很快变得过于复杂。那么,我该如何让它发挥作用呢?

EDIT1:我尝试按照建议使用SetThreadDpiAwarenessContext(),但它仍然不起作用。现在,当我将窗口移动到 (3000,0) 时,它改为移动到 (4500,0)。似乎我需要以某种方式缩放传递给SetWindowPos() 的坐标,但我不知道如何。

m_previousContext = SetThreadDpiAwarenessContext(GetWindowDpiAwarenessContext(hwnd));
if(m_previousContext == NULL)
  Log::out<Log::Level::Error>("Failed to set thread dpi awareness context.");
SetWindowPos(hwnd, HWND_BOTTOM, 3000, 0, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE);

另外,如果我定期调整窗口大小和四处移动,这不是效率很低吗?

EDIT2:我附上了一个指向最小工作二进制文件的链接。你可以download it from Google Drive here。它需要 Windows 10 版本 1607 才能运行。当我使用SetWindowPos.exe 002108B6 3000 0 在上面提到的设置上运行它时,窗口将移动到 (4500,0)。

下面是代码:

int main(int argc, char** argv)
{
  if(argc < 4)
  {
    std::cerr << "Usage: SetWindowPos.exe <HWND> <x> <y>" << std::endl;
    return 1;
  }
  HWND hwnd;
  int x, y;
  try
  {
    hwnd = (HWND)hexStrToInt(argv[1]); // I've omitted the implementation of hexStrToInt
    x = atoi(argv[2]);
    y = atoi(argv[3]);
  }
  catch(...)
  {
    std::cerr << "Invalid arguments." << std::endl;
    return 1;
  }
  if(IsWindow(hwnd) == FALSE)
  {
    std::cerr << "Invalid window handle " << argv[1] << "." << std::endl;
    return 1;
  }
  auto context = SetThreadDpiAwarenessContext(GetWindowDpiAwarenessContext(hwnd));
  SetWindowPos(hwnd, HWND_BOTTOM, x, y, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE);
  SetThreadDpiAwarenessContext(context);
  return 0;
}

【问题讨论】:

  • 你有想过这个吗?
  • 不幸的是,并非如此。我很快会再次调查。
  • 我也遇到了PhysicalToLogicalPointForPerMonitorDPI 的问题。该函数不执行任何操作,即使它返回TRUE。所以我在 Chromium 源代码中搜索了它,看看他们是如何使用它的。但事实证明,他们根本不使用该功能。相反,他们使用GetDpiForMonitor 来计算相应监视器的缩放因子。

标签: c++ windows winapi dpi multiple-monitors


【解决方案1】:

【讨论】:

  • 感谢您的回答。不幸的是,这仍然行不通。有关详细信息,请参阅我编辑的问题。
  • 更改上下文后,您需要重新读取新上下文中的所有监视器、窗口、指针坐标,计算新上下文中的坐标并将它们提供给SetWindowPos(记住新上下文)。从一个上下文计算到另一个上下文可能很困难,因为可能存在差距或重叠。
  • 正如您在第二次编辑中看到的那样,我什至没有阅读任何显示器或类似设备。
猜你喜欢
  • 2019-03-31
  • 2014-05-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多