I found two ways to achieve this.
With this approach, we MoveWindow
twice. The first time we move it will position the window and calculate all its size information. This will allow us to get two aspects of its dimensions. And the second time, we'll take into account the "drop shadow" border. For this to work, we need to ShowWindow( SW_SHOW );
before we 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 );
}
This approach seemed to work better than the other.
Using this method, the idea is to MoveWindow
before it is shown.
void MyCFrameWndMain::applyWindowSizeAndShow()
{
int targetWidth = ::GetSystemMetrics( SM_CXSCREEN );
int targetHeight = ::GetSystemMetrics( SM_CYSCREEN );
MoveWindow( 0, 0, targetWidth, targetHeight, TRUE );
ShowWindow( SW_SHOW );
}
and then handling the WM_NCCALCSIZE
message:
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;
// ...
}
}
The drawbacks I found using this method:
- there was a one pixel line between the top edge of the window and the top edge of the screen
- when I had two monitors that did not use the same scaling (e.g. the main one being at 150% and the second being at 100%), when I dragged the window using the title bar from one monitor to the other, the title bar would vanish and everything would be all messed up. I have not been able to reproduce this issue when both were at 100%. This should not be an issue when the typical usage is to not move the window (which is our case).
- I suppose I'm not too sure how this all works, but it seemed that some of the window elements were a bit out of place.
If I had not been able to find the first method, this second method would have worked well enough for our needs, even if it has a couple of issues.
I've used those answers (among other resources):