running the form below and continuously scrolling the treeview up and down will freeze the form on (my) Windows 10. The scrollbar, the form title bar and its buttons all become unresponsive, but will still update on timer events. Clicking on treeview still works normally. On (my) Windows 7, the freezing does not happen.
On (my) Windows 10 When you click or Alt-tab away from the application and back, form becomes responsive again. Which means that whenever I switched away to Delphi IDE to pause and see what was going on, problem was gone. On one occasion I did manage to get form so stuck that switching away from it to debugger did not unfreeze it, and the Call Stack was deep inside UxTheme.dll.
As you can see in the code I have a sort of a workaround but it is not very satisfying. Can anybody explain what is going on here ?
I did my best to make the code sample below easy to run from scratch, that's why I included the .dpr The problem originates from a much more complex form updated from a background thread.
Update: just tried same sort of thing with TListView and I get no freezing.
Update: as nolaspeaker suggested, compiling without "enable runtime themes" fixes the problem.
Update: my original complex form was not fixed by compiling without "enable runtime themes" - as the app uses a custom manifest. However using DisableThemesApp from here How to switch an Application between Themed and not Themed at run-time? helped. In fact skipping STAP_ALLOW_NONCLIENT was enough. The only downside being, on Windows 10 the app now looks like something from Deliverance - i.e. inbred :). So I will continue using my GetLastInputInfo bodge until someone suggests something better.
unit Win10Freezing;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.ComCtrls;
type
TWin10FreezingForm = class(TForm)
private
FTreeView: TTreeView;
procedure TimerTimer(Sender: TObject);
function HasASecondPassedSinceLastInput: boolean;
public
constructor Create(AOwner: TComponent);override;
end;
var
Win10FreezingForm: TWin10FreezingForm;
implementation
{$R *.dfm}
constructor TWin10FreezingForm.Create(AOwner: TComponent);
begin
inherited;
Width := 355;
Height := 355;
FTreeView := TTreeView.Create(self);
with FTreeView do begin
Parent := self;
Align := alClient
end;
with TTimer.Create(self) do begin
OnTimer := TimerTimer;
Interval := 2000;
Enabled := TRUE
end;
TimerTimer(self)
end;
procedure TWin10FreezingForm.TimerTimer(Sender: TObject);
var
i: Integer;
begin
//once the TreeView has been populated
//continuously scroling the listView up and down will freeze the form
//uncomment the following line as a not very good workaround
//if HasASecondPassedSinceLastInput then
with FTreeView.Items do begin
BeginUpdate;
try
Clear;
for i := 0 to 30 + Random(10) do
AddChild(nil, IntToStr(Random(100)))
finally
EndUpdate
end
end
end;
function TWin10FreezingForm.HasASecondPassedSinceLastInput: boolean;
var lii: TLastInputInfo;
begin
lii.cbSize := SizeOf(TLastInputInfo);
result := GetLastInputInfo(lii) and (GetTickCount - lii.dwTime > 1000)
end;
end.
program Win10Freeze;
uses
Vcl.Forms,
Win10Freezing in 'Win10Freezing.pas' {Win10FreezingForm};
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TWin10FreezingForm, Win10FreezingForm);
Application.Run;
end.