A.M. Hoornweg 144 Posted May 7, 2019 Hello all, In a certain application of mine that contains a tDrawGrid, the program hangs if I drag the form between two screens with different resolutions. The program becomes totally unresponsive and I need to kill it with the task manager. This crash only happens if the grid contains a large number of rows and columns. It is compiled with Delphi Rio 10.3.1. I can work around it by setting the rowcount to a very small number immediately before the DPI change and then restore it afterwards. Kind regards, Arthur Share this post Link to post
eivindbakkestuen 47 Posted May 7, 2019 So, what can you see under the debugger when the app is unresponsive? Millions of event calls? Loop that never ends somewhere? If you can boil it down to a small reproducible testapp, log it in quality portal, post link here for others to vote. Share this post Link to post
A.M. Hoornweg 144 Posted May 8, 2019 In the debugger I can break the program, but the current IP is always somewhere inside a Windows DLL and the call stack has no entries. I tried to make a reproducible test app, but no luck. Share this post Link to post
Микола Петрівський 10 Posted May 8, 2019 6 minutes ago, A.M. Hoornweg said: In the debugger I can break the program, but the current IP is always somewhere inside a Windows DLL and the call stack has no entries. I tried to make a reproducible test app, but no luck. Do not forget, that any app nowadays has multiple threads, and when you break the program, currently selected thread does not necessarily will be VCL main thread. You need to select correct thread in Threads window. Share this post Link to post
A.M. Hoornweg 144 Posted May 9, 2019 When the freeze happens, all windows on the desktop (including the Delphi IDE) become unresponsive to mouse clicks and keyboard events. I need to press ctrl-alt-del to get into the task manager, only then am I able to set a breakpoint in the Delphi IDE. I managed to break into the main thread of the program and set a breakpoint there. The breakpoint itself is in GDI32.InternalDeleteDC() and the base of the call stack is in USER32.SetScrollInfo. Continuing the program then triggers the same breakpoint in an endless loop. If I simply remove the tDrawGrid from the form then the problem disappears and I can move the form between displays with different DPI without any freezing. If I set the grid's rowcount to a very low number, there is no problem either. My workaround for now is to temporarily set the rowcount to 1 just before the dpi change. Share this post Link to post
A.M. Hoornweg 144 Posted May 9, 2019 I have found the core of the issue. When a form receives a wm_dpichanged message, TCustomForm.WMDpiChanged is called which scales all components on the form. But TCustomGrid does it in a terrible way; there's this simple loop that modifies the heights of the rows: [vcl.grids, TCustomGrid.ChangeScale(), line 1735] for I := 0 to RowCount -1 do RowHeights := MulDiv(RowHeights, M, D); and in the debugger I see that this loop needs a whopping 72 seconds to complete on my Core i7 notebook (for 86400 rows). The reason? It appears that the changing of a single row height triggers an InvalidateGrid() plus an update of the scroll bars (which, incidentally, is why my breakpoints kept landing in USER32.SetScrollInfo). And all of this happens inside the handling of a wm_dpichanged message, so the message queue is blocked for ages which freezes my desktop. 3 Share this post Link to post
PeterBelow 239 Posted May 9, 2019 4 minutes ago, A.M. Hoornweg said: I have found the core of the issue. When a form receives a wm_dpichanged message, TCustomForm.WMDpiChanged is called which scales all components on the form. But TCustomGrid does it in a terrible way; there's this simple loop that modifies the heights of the rows: [vcl.grids, TCustomGrid.ChangeScale(), line 1735] for I := 0 to RowCount -1 do RowHeights := MulDiv(RowHeights, M, D); and in the debugger I see that this loop needs a whopping 72 seconds to complete on my Core i7 notebook (for 86400 rows). The reason? It appears that the changing of a single row height triggers an InvalidateGrid() plus an update of the scroll bars (which, incidentally, is why my breakpoints kept landing in USER32.SetScrollInfo). And all of this happens inside the handling of a wm_dpichanged message, so the message queue is blocked for ages which freezes my desktop. Excellent analysis! Would you please log a bug report for this problem on quality.embarcadero.com? Share this post Link to post
A.M. Hoornweg 144 Posted May 9, 2019 Done, https://quality.embarcadero.com/browse/RSP-24430 3 Share this post Link to post
Guest Posted May 9, 2019 (edited) Since you have the sources, you can add the VCL unit to your project (even copy it to another folder) and put BeginUpdate, EndUpdate around that loop. Might help. At least it will (if it's the solution) show you that your fix works, and you can try different approaches. Edited May 9, 2019 by Guest Share this post Link to post
A.M. Hoornweg 144 Posted May 9, 2019 Unfortunately tCustomgrid has no methods beginupdate / endupdate. Share this post Link to post
PeterBelow 239 Posted May 10, 2019 20 hours ago, A.M. Hoornweg said: Unfortunately tCustomgrid has no methods beginupdate / endupdate. You can send a WM_SETREDRAW message to the control to block painting, but that may not affect the updating of the scrollbar range. Share this post Link to post
Dmitry Arefiev 106 Posted May 11, 2019 @A.M. Hoornweg, I created and attached experimental patch. Please, try it and let me know. If all is OK, then I will include it into next 10.3 update. grid.patch 5 Share this post Link to post
A.M. Hoornweg 144 Posted May 14, 2019 Hi Dmitry, as far as I can see, the patch works! 1 Share this post Link to post