I'm working on a legacy (1999) codebase, which has an annoying bug where scrolling in some GUI elements doesn't redraw properly when the window is placed on a non-primary monitor.
As far as I can make out (not being very familiar with Windows's APIs) the problem is that the code fetches the DeviceContext to draw to using GetDC(hwnd)
, which from what I can make out of the docs gets a DC for the primary monitor only (but the docs aren't terribly clear, TBH).
I've managed to draw things on the screen, using basically:
RECT rect;
GetWindowRect(hwnd, &rect);
HMONITOR monitor = MonitorFromRect(&rect, MONITOR_DEFAULTTOPRIMARY);
MONITORINFOEX minfo;
minfo.cbSize = sizeof(MONITORINFOEX);
GetMonitorInfo(monitor, &minfo);
HDC = CreateDC(NULL, minfo.szDevice, NULL, NULL);
This gets things drawn to the correct monitor, but the application clearly expects something that's window-relative, because everything ends up in the top-left corner of the desktop and not in the window.
Now, my googling seems to indicate that running the paint code twice using EnumDisplayMonitors(GetDC(), &rect, PaintCallback, NULL)
should do the right thing. Unfortunately, my code isn't C or C++. It's a SmallTalk image (and support ended 15 years ago or so, so complaining to the vendor is out), and I'm simply not sure if the paint handling happens in code that I have access to, or if it's deep enough in the guts of the SmallTalk that I can't get to it.
Thus my question: is it possible to create a DC that's relative to my window's client area (by tweaking the DC from CreateDC perhaps)? I realize this will probably break if the window straddles two monitors, but that's at least less broken than the current state of things.
UPDATE:
I've managed to run the rendering code twice, using EnumDisplayMonitors
, but that crashes in weird ways (which is most likely a SmallTalk problem; the compiler is old and idiosyncratic, but debugging code this deep in the stack is problematic).
To answer the comments: I think the code basically wants to draw on windows, yes. The SmallTalk objects representing the various GUI elements carry around window handles that are used to create DCs with GetDC(hwnd)
, so that's easy enough. So it sounds like GetDC(hwnd)
should get a DC that does the right thing, in this case; it could be that the ST code is caching the DCs somewhere and GetDC
will return a different DC when the window is moved to a different screen (which sounds plausible from my cursory knowledge of this).
It looks like the problem was indeed caching of the DCs created by GetDC
. I hacked the rendering code to not use the caches (more or less, the code is a bit tangled), and from a first look it looks like that did the trick.
For posterity, in case someone googles for Visual Smalltalk Enterprise and finds this answer, what I did was to edit GraphicsTool>>ifNilHandle:
to not actually check if the handle is nil, but always run the block to get a fresh handle and return true at the end. This way, fresh DCs are fetched every time.
UPDATE:
This fixes the immediate problem I was trying to fix, but unfortunately breaks a couple of other things (most notably WindowBuilder Pro, the GUI construction tool-kit). The problem is clearly over-eager caching of DCs somewhere in the code, but it needs to be fixed in a much more focussed manner than the sledgehammer approach above.