2

I'm working on making an MFC app display properly on multiple monitor environments with different DPI scaling. There is one issue I cannot explain which occurs when the primary and secondary monitors are running with different DPIs and the app is on the secondary monitor.

If the primary monitor has 100% DPI scaling (96) and the secondary monitor has the same 100% DPI scaling, all is fine.

If the primary monitor has 100% DPI scaling (96) and the secondary monitor has 125% scaling (120 DPI) or 150% scaling (144 DPI) or any other higher value, when child windows are maximized, part of the child window system bar is visible, as seen here:

125% scaling: enter image description here

150% scaling: enter image description here

If you look carefully, its 7 pixels for 125% and 14 for 150%. Given that the system bar is 29 pixels at 100% scaling, 36 and 125%, and 43 at 150%, those 7 and 14 pixels is the height difference between the bar size at 125% and 150% respectively, compared to the 100% baseline.

Therefore, it appears that the position and size of the bar is computed by the system as it was run on the primary monitor.

When you maximize the child window, there is a series of Windows messages that are sent to the window: WM_GETMINMAXINFO > WM_WINDOWPOSCHANGING > WM_GETMINMAXINFO > WM_NCCALSIZE > WM_WINDOWSPOSCHANGED > WM_MOVE > WM_SIZE. WM_GETMINMAXINFO is sent when the size or position of the window is about to change so that an app can override, for instance, the window's default maximized size and position. There is a note about this:

For systems with multiple monitors, the ptMaxSize and ptMaxPosition members describe the maximized size and position of the window on the primary monitor, even if the window ultimately maximizes onto a secondary monitor. In that case, the window manager adjusts these values to compensate for differences between the primary monitor and the monitor that displays the window. Thus, if the user leaves ptMaxSize untouched, a window on a monitor larger than the primary monitor maximizes to the size of the larger monitor.

There is an article by Raymond Chan, explaining this: How does the window manager adjust ptMaxSize and ptMaxPosition for multiple monitors?.

So the ptMaxSize should be filled with the dimensions of the primary monitor. My primary monitor is 2560x1440 pixels and the size of the secondary monitor is 1920x1200. However, the value of the size I get here is 1757x1023 and 1761x1027 (at consecutive calls). This is neither the size of the primary nor the secondary monitor.

I tried to do a dirty trick and handled the WM_NCCALCSIZE message and set the position (left, top) at 0 (relative to the parent).

void CMyMDIChildWnd::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp)
{
   CMDIChildWnd::OnNcCalcSize(bCalcValidRects, lpncsp);   
   if (condition)
   {
      lpncsp->rgrc[0].left = 0;
      lpncsp->rgrc[0].top = 0;
   }
}

Works fine as long as the child window has the focus. If I click on another window and it loses the focus, then the bar is redrawn and shows up at the previous position. This trick is only saying where the client area starts so when the non-client is redrawn I get back to the original problem.

My question is what could the root of this problem be and how can I try to fix it?

Marius Bancila
  • 16,053
  • 9
  • 49
  • 91
  • 1
    There are many many ways to program MFC apps. I don't reproduce any issue with standard MFC template with or without Per Monitor High DPI Aware setting and two different monitor. But your blue "ribbon" with 2 icons doesn't look standard. Do you have a reproducing code/project? – Simon Mourier Apr 28 '21 at 16:34
  • No, I don't have a reproducible code. I made a MDI MFC app and maximizing child windows works fine. The UI of my app is customized entirely. But it's not that special. The blue bar is the menu bar and under it are the toolbars. I didn't screenshot the entire window, just the relevant part. – Marius Bancila Apr 28 '21 at 19:32
  • I'm sorry I can't build the project to reproduce your problem, so I still hope you can provide a reproducible sample. Maybe you can refer to [this](https://stackoverflow.com/questions/1164868/how-to-get-size-of-check-and-gap-in-check-box/59376905#59376905) possibly related thread. – Zeus Apr 29 '21 at 02:27
  • 1
    I'm guessing that you have some problems with non client area handling for __main form__. – Daniel Sęk Apr 29 '21 at 07:41
  • Actually, the main frame does not handle the `WM_NCPAINT` message nor other non-client message (neither `WM_PAINT` nor `WM_ERASEBKGND` for that matter). It's not much going on there. – Marius Bancila Apr 29 '21 at 09:40
  • You have to put more work into it. Try to reproduce this behavior in standard MDI application generated by MFC Wizard, compare coordinates by using Spy++. For example height of top window frame (caption and sizer) on my computer (Windows 10, 96DPI) is 31 pixels (you use 29). Unfortunatelly we can't help you, because it seems that applications generated by MFC Wizard don't have this probem. – Daniel Sęk Apr 29 '21 at 16:16

1 Answers1

0

I came across possibly the same phenomenon, albeit from a different route, and my solution was very similar to yours, with a little extra, maybe this might help you?

void CFixedFrame::OnNcCalcSize ( BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp )
{
    MDIBASEWND::OnNcCalcSize ( bCalcValidRects, lpncsp ) ;

    if ( bCalcValidRects )
    {
        RECT& rcNew = lpncsp->rgrc[0];
        RECT& rcOld = lpncsp->rgrc[1];
        RECT& rcClient = lpncsp->rgrc[2];

        // My problem arose because of the Aero bug (hardwired border widths)
        // And also problems with Theming
        const CNonClientMetrics ncm;
        rcNew.top = ncm.iCaptionHeight + Aero related stuff not relevant to your problem
    }
}

Where CNonClientMetrics is...

class CNonClientMetrics : public NONCLIENTMETRICS
{
public:
    CNonClientMetrics ( )
    {
        cbSize = sizeof ( NONCLIENTMETRICS ) - sizeof ( this->iPaddedBorderWidth ) ;
        SystemParametersInfo ( SPI_GETNONCLIENTMETRICS, sizeof ( NONCLIENTMETRICS ), this, 0 ) ;
    }
} ;

[In a DPI-Aware way] This allowed me to get rid of that annoying border of blue (with buttons). As I understand it, you can no longer disable the DWM that draws that section.

I can't now find my original references, but in my notes for this problem, it only occurs for MFC's MDI Frame Windows.

Still, this link might also be useful?

dicksters
  • 106
  • 5