如何调整Windows 10窗口的大小并忽略透明边框?

时间:2017-12-06 17:13:52

标签: c++ window windows-10

我们的3D应用程序窗口需要覆盖整个用户桌面(不可调整大小,不是最大化,而不是全屏,如电子游戏中所见,只是一个窗口右上角的X,标题栏和状态栏。

为实现这一目标,我们使用了以下代码:

void
CTheApp::SetFullScreenMode()
{
  HWND hwnd = getHandle();

  long style = ::GetWindowLong(hwnd, GWL_STYLE);
  style &= ~(WS_MAXIMIZEBOX|WS_MINIMIZEBOX|WS_THICKFRAME);
  ::SetWindowLong(hwnd, GWL_STYLE, style);

  // get screen dimensions
  int width  = ::GetSystemMetrics(SM_CXSCREEN);
  int height = ::GetSystemMetrics(SM_CYSCREEN);

  ::MoveWindow(hwnd, 0, 0, width, height, TRUE);
}

它满足了我们的需求......直到Windows 10到来。

对于Windows 10,窗口没有覆盖左侧,右侧和底部的小间隙。 (更准确地说:左边9个像素,右边9个像素,底部9个像素。)

似乎Windows 10将此透明样式添加到非最大化窗口的边框。

我怎样才能使窗口在Windows 10的整个屏幕上正确显示(不留明显空隙),同时保持该功能对以前版本的Windows(Windows 7 - Windows 8.1)有效?是否有一种干净的方法将此“透明度”设置为零?

我考虑过将其破解并将每个尺寸扩大9个像素,但这可能会在用户设置另一个主题时出现问题(例如高对比度),或者当应用在多个监视器上使用多个窗口时。

2 个答案:

答案 0 :(得分:2)

我找到了两种方法来实现这一目标。

通过使用DwmGetWindowAttribute函数

使用这种方法,我们MoveWindow两次。我们第一次移动它时,将定位窗口并计算其所有尺寸信息。这将使我们能够获得其维度的两个方面。第二次,我们将考虑“阴影”边框。为此,我们需要ShowWindow( SW_SHOW );之前MoveWindow

void MyCFrameWndMain::applyWindowSizeAndShow()
{
  ShowWindow( SW_SHOW );
  
  int targetWidth = ::GetSystemMetrics( SM_CXSCREEN );
  int targetHeight = ::GetSystemMetrics( SM_CYSCREEN );

  MoveWindow( 0, 0, targetWidth, targetHeight, TRUE );
  
  RECT decoratedRect;
  // This gets the rectangle "with the decoration":
  GetWindowRect( &decoratedRect ); 
  RECT leanRECT;
  // This gets the rectangle "without the decoration":
  ::DwmGetWindowAttribute( m_hWnd, DWMWA_EXTENDED_FRAME_BOUNDS, &leanRECT, sizeof( leanRECT ) ); 
  
  int leftDecoration   = leanRECT.left        - decoratedRect.left;
  int rightDecoration  = decoratedRect.right  - leanRECT.right;
  int topDecoration    = leanRECT.top         - decoratedRect.top;
  int bottomDecoration = decoratedRect.bottom - leanRECT.bottom;
  
  // Apparently, the "rectangle without the decoration" still has a 1 pixel border on the sides and 
  // at the bottom, so we use the GetSystemMetrics to figure out the size of that border, and 
  // correct the size.
  int xBorder = ::GetSystemMetrics( SM_CXBORDER );
  int yBorder = ::GetSystemMetrics( SM_CYBORDER );

  MoveWindow( 
    0 - leftDecoration - xBorder
    , 0 - topDecoration - yBorder
    , targetWidth + leftDecoration + rightDecoration + 2*xBorder
    , targetHeight + topDecoration + bottomDecoration + 2*yBorder
    , TRUE );
}

这种方法似乎比其他方法更好。

通过处理WM_NCCALCSIZE消息

使用此方法,其想法是在显示之前MoveWindow

void MyCFrameWndMain::applyWindowSizeAndShow()
{
  int targetWidth = ::GetSystemMetrics( SM_CXSCREEN );
  int targetHeight = ::GetSystemMetrics( SM_CYSCREEN );

  MoveWindow( 0, 0, targetWidth, targetHeight, TRUE );

  ShowWindow( SW_SHOW );
}

,然后处理WM_NCCALCSIZE消息:

LRESULT MyCFrameWndMain::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
  switch (message)
  {
  
  // ...
  
  case WM_NCCALCSIZE:
  { // https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-nccalcsize?redirectedfrom=MSDN
    int titleBarHeight = ::GetSystemMetrics( SM_CYCAPTION );
    if ( wParam == TRUE )
    {
      NCCALCSIZE_PARAMS* params = reinterpret_cast<NCCALCSIZE_PARAMS*>( lParam );
      params->rgrc[0].top = titleBarHeight;
      return WVR_REDRAW;
    }
    else //if ( wParam == FALSE )
    {
      RECT* rect = reinterpret_cast<RECT*>( lParam );
      rect->top = titleBarHeight;
      return 0;
    }
  }
  break;

  // ...

  }
}

使用此方法发现的缺点:

  • 窗口的顶部边缘和屏幕的顶部边缘之间只有一条像素线
  • 当我有两台显示器没有使用相同的缩放比例时(例如,主显示器的缩放比例为150%,第二台显示器的缩放比例为100%),当我使用标题栏将窗口从一台显示器拖动到另一台显示器时,标题栏将消失,所有内容都将被弄乱。当两者均为100%时,我无法重现此问题。当典型用法是不移动窗口时(这就是我们的情况),这应该不是问题。
  • 我想我不太确定这一切如何工作,但是似乎某些窗口元素有些不合适。

如果我找不到第一种方法,那么即使有很多问题,第二种方法也可以很好地满足我们的需求。


我已经使用了这些答案(在其他资源中):

答案 1 :(得分:1)

这由Desktop Window Manager处理,可以覆盖。主要步骤是WM_NCCALCSIZE(窗口消息,非客户端),您覆盖它以返回0,0