If you’ve been using GExperts’ “Edit Unit Search Path” dialog on a multi-monitor setup with mixed DPI scaling, you may have noticed something odd: The dialog gets a little bigger every time you open it. Not dramatically – just enough to be annoying. Open it ten times and it’s noticeably larger. Open it twenty times and it’s taking over your screen.
This has been bugging me for ages.
The Setup
The problem only appears with mixed-DPI monitors. In my case: The main monitor runs at 100% scaling (96 DPI) while the IDE sits on a secondary monitor at 125% scaling (120 DPI). Single-monitor setups or setups where all monitors use the same scaling are unaffected.
What Was Happening
GExperts saves and restores dialog positions and sizes so they reappear where you left them. When the IDE is DPI-aware (Delphi 11+), it also save the monitor’s DPI alongside the pixel dimensions, so it can scale correctly if the DPI changes between sessions.
The restore code looked like this:
OrigDpi := TScreen_GetDpiForForm(Form); // ... read saved DPI, position, size ... if NewDpi <> OrigDpi then begin NewWidth := MulDiv(NewWidth, NewDpi, OrigDpi); NewHeight := MulDiv(NewHeight, NewDpi, OrigDpi); end;
Two bugs were hiding in these few lines.
Bug 1: Wrong reference DPI. TScreen_GetDpiForForm returns the DPI of the monitor where the form currently is – but at this point during creation, the form hasn’t been moved to its saved position yet. It’s still on the primary monitor (96 DPI). The saved position is on the 125% monitor (120 DPI). So OrigDpi is 96 when it should be 120.
Bug 2: Reversed scaling direction. MulDiv(NewWidth, NewDpi, OrigDpi) scales from OrigDpi to NewDpi. But NewWidth is already in NewDpi pixels (that’s the DPI it was saved at). It should be scaling from NewDpi to the target DPI.
Together, these two bugs meant: saved width W (at 120 DPI) was scaled by MulDiv(W, 120, 96) = W x 1.25. The dialog ended up 25% too large. On close, this larger size was saved. Next open: W x 1.25 x 1.25. And so on.
The Fix
if PosChanged then OrigDpi := TScreen_GetDpiForPoint(Point(NewLeft, NewTop)); if (NewDpi > 0) and (OrigDpi > 0) and (NewDpi <> OrigDpi) then begin NewWidth := MulDiv(NewWidth, OrigDpi, NewDpi); NewHeight := MulDiv(NewHeight, OrigDpi, NewDpi); end;
When restoring a saved position, it now looks up the DPI of the target monitor (the one at the saved coordinates) instead of the form’s current monitor. And the MulDiv arguments are in the right order.
In the common case – same monitor setup as last time – the saved DPI equals the target monitor’s DPI, so no scaling happens at all. The saved pixel values are simply used as-is, which is exactly what we want.
Why It Took So Long
I actually thought I’d fixed this once before. That attempt broke things for everyone on single-monitor or same scaling setups – the most common configuration. DPI scaling code is treacherous: It’s easy to write something that fixes your specific multi-monitor scenario while subtly breaking the straightforward case. This time, the key insight was that when the DPIs match (which they always do on single-scaling setups), the scaling code is skipped entirely, so it can’t break anything.
The fix affects all GExperts dialogs that save their position, not just Edit Search Path. If you’ve been quietly resizing other dialogs back to normal after every few uses – that should be over now.
Discussion about this in the corresponding post in the international Delphi Praxis forum.