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

3
我们的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像素)。

看起来Windows 10在非最大化窗口的边框上添加了这种透明样式。

有没有一种简单的方法,在 Windows 10 中使窗口充满整个屏幕而不留下任何明显的间隙(即消除透明边框),同时又可以在先前版本的 Windows 中(Windows 7 - Windows 8.1)保持软件功能的有效性?是否可以通过某种方式将这种“透明度”设置为零?


我考虑过通过向每个方向增加9个像素的方式进行“破解”,但这可能会导致用户选择其他主题(例如高对比度)或者该应用程序在多个显示器上使用多个窗口时出现问题。


https://msdn.microsoft.com/en-us/library/windows/desktop/ms632626(v=vs.85).aspx - Hans Passant
请参考此答案:https://dev59.com/ulwY5IYBdhLWcg3w2Ksv - Dwedit
@dwedit 谢谢,我回到那个任务时会检查一下。 - Vaillancourt
2个回答

3
我找到了两种方法实现这一点。

使用DwmGetWindowAttribute函数

通过这种方法,我们会MoveWindow两次。第一次移动窗口时将定位窗口并计算其所有大小信息。这样可以让我们获得其尺寸的两个方面。第二次,我们将考虑“投影”边框。为了使其正常工作,我们需要在MoveWindow之前ShowWindow(SW_SHOW);

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://learn.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
这由桌面窗口管理器处理,可以被覆盖。主要步骤是WM_NCCALCSIZE(窗口消息,非客户端),你需要覆盖它并返回0,0。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接