具有多个监视器和不同显示比例的SetWindowPos()交叉处理

时间:2019-02-18 16:06:06

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

我已经问过类似的问题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)。坐标似乎已缩小为逻辑坐标。

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

现在看来,我只是无法将窗口移动到左上方在主屏幕上的任何位置,但是窗口的中心仍在辅助屏幕上,因为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;
}

2 个答案:

答案 0 :(得分:0)

答案 1 :(得分:-1)

不仅您需要将线程感知模式设置为相同的值,还需要将它们设置为DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2才能同时启用dpi感知。

在您的应用程序和目标应用程序中都设置它:

SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);