Jump to content
A.M. Hoornweg

DPI-change, crashes tDrawgrid.

Recommended Posts

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

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

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
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

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

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. 

 

 

  • Like 1
  • Thanks 3

Share this post


Link to post
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
Posted (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 by Dany Marmur

Share this post


Link to post
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

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×