Remove resize border and drawn in the occupied area?

Viewed 280

When I remove the sizing border of a window, it leaves black areas around it.

Is it possible to somehow tell the window to occupy/paint these areas, as there's no sizing border anymore?

A reproducible example: launch a instance of chrome browser with: chrome.exe -app=https://www.google.com

Remove the sizing border:

LONG lStyle = GetWindowLong(hwnd, GWL_STYLE);
lStyle &= ~(WS_THICKFRAME | WS_DLGFRAME);
SetWindowLong(hwnd, GWL_STYLE, lStyle);

WS_THICKFRAME
0x00040000L
The window has a sizing border. Same as the WS_SIZEBOX style.

WS_DLGFRAME
0x00400000L
The window has a border of a style typically used with dialog boxes. A window with this style cannot have a title bar.

The black areas im talking about: left, right, bottom.

image

As suggested, I use SetWindowPos() with SWP_FRAMECHANGED, but it didn't result in any visible change.

int flags = SWP_NOSENDCHANGING | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED;
SetWindowPos(hWnd, 0, 0, 0, 0, 0, flags);

Code:

#include <iostream>
#include <windows.h>

int main()
{   
    HWND hwnd = FindWindowW( L"Chrome_WidgetWin_1", nullptr );
    //HWND hwnd = FindWindowW( L"Notepad", nullptr );

    std::cout << "hwnd: " << hwnd << "\n";

    LONG lStyle = GetWindowLong( hwnd, GWL_STYLE );
    lStyle &= ~( WS_THICKFRAME | WS_DLGFRAME );
    SetWindowLong( hwnd, GWL_STYLE, lStyle );

    int flags = SWP_NOSENDCHANGING | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED;
    auto swp = SetWindowPos( hwnd, 0, 0, 0, 0, 0, flags );

    std::cout << "swp: " << swp << "\n";
}
1 Answers

TL;DR

Considering that it works for notepad, it must be a specialty of Google Chrome. The difference should be that Chrome is drawing into the non-client area of the window (i.e. the tabs appear in the title bar, which is kind of special). That means that Chrome is doing a lot of drawing and computations by itself. Apparently, the code does not handle the case that the window has no sizing borders (because Chrome by itself does not support this). Instead, they hardcoded that the client rectangle is always smaller by the sizing border width, even if there is no sizing border.

A workaround would be to start Chrome with --disable-dwm-composition. But I do not really know if there are any unintended side effects when using this option.

Details

Digging through the source code of Chromium, one can see:

  • First note that the WinAPI function DwmExtendFrameIntoClientArea() is called, which means that Chromium has indeed the special drawing logic to draw the window frame by itself.
  • To remove the standard frame, the WM_NCCALCSIZE message is handled. Therein, we can find a call to GetClientAreaInsets(), which returns the "insets", i.e. 4 values (for left, top, bottom and right) by which the client rectangle becomes smaller.
  • Searching for GetClientAreaInsets(), we see that its implementations essentially calls ui::GetFrameThickness() and uses its return value as the inset value. But only if the window is not in fullscreen; indeed, the black border effect does not appear in fullscreen.
  • ui::GetFrameThickness() calls (indirectly) the WinAPI function GetSystemMetricsForDpi() to get the values of SM_CXSIZEFRAME and SM_CXPADDEDBORDER, i.e. the size of the sizing border. Their sum is returned.
  • The SM_CXSIZEFRAME and SM_CXPADDEDBORDER apparently map to the registry values BorderWidth and PaddedBorderWidth in HKEY_CURRENT_USER\Control Panel\Desktop\WindowMetrics. I.e. they are system settings. Changing these values (to a notable more negative value) and restarting the PC does not make the drawn border of windows larger on Windows 10 (although it did in earlier Windows versions). But they do change the invisible "capture" area of the sizing borders. And, in our case here, the black borders shown by Chrome also become larger.
  • I think the former sizing border areas are drawn black after removing them because of the BLACK_BRUSH in HWNDMessageHandler::OnPaint.

So, in a nutshell, Chrome is simply making the client area smaller by the sizing borders, regardless if the sizing borders actually exist or not. And then draws everything in there. If Chrome were supporting removed sizing borders officially, it could be considered as a bug. But I don't think they do.

How do we work around this behavior from the "outside"?

  • Considering that SM_CXSIZEFRAME and SM_CXPADDEDBORDER are global settings, changing them (by means of the registry or the SystemParametersInfo(SPI_GETBORDER,...) function is probably out of the question for practical uses.
  • But looking through the source code, we note that in HWNDMessageHandler::UpdateDwmFrame() the function ui::win::IsAeroGlassEnabled() is called, which in turn returns false if the disable-dwm-composition commandline flag is specified. Hence, starting Chrome with the --disable-dwm-composition flag and then running the code in the OP (removing WS_THICKFRAME | WS_DLGFRAME) does the trick for me. The invisible sizing borders are truly gone, i.e. clicks onto the space where they were are no longer registered by Chrome but by the window below it (e.g. the Desktop).
Related