1

Sometimes I got EStackOverflow exception in my project. I use Delphi 2010 and latest version of VirtualTreeView. Report generated by Eurekalog contains infinite loop like this:

(this is a part of "Call stack" section of bugreport)

SetNodeHeight            
MeasureItemHeight      
GetNodeHeight          
GetDisplayRect         
InvalidateToBottom     
SetNodeHeight          
MeasureItemHeight
GetNodeHeight          
GetDisplayRect         
InvalidateToBottom     
SetNodeHeight          
MeasureItemHeight      
GetNodeHeight          
GetDisplayRect       

All this lines is in VirtualTrees.pas, internal module of VirtualTreeView

screenshot

The event handlers attached to the control are:

  • TreeChange
  • TreeCollapsing
  • TreeFocusChanging
  • TreeFreeNode
  • TreeGetHint
  • TreeMeasureItem

procedure TTrainingForm.TreeMeasureItem(Sender: TBaseVirtualTree;  
  TargetCanvas: TCanvas; Node: PVirtualNode; var NodeHeight: Integer);  
begin  
  inherited;  
  if Sender.MultiLine[Node] then  
  begin  
    try  
      TargetCanvas.Font := Sender.Font;  
      NodeHeight := TVirtualStringTree(Sender).  
         ComputeNodeHeight(TargetCanvas, Node, 0) + 4;  
    except  
      NodeHeight := 24;  
    end;  
  end  
end;  

I can't reproduce bug, but it happens sometimes in several sites.

How can I fix or workaround this problem?

TLama
  • 75,147
  • 17
  • 214
  • 392
  • What is before that loop? The call which precedes it might give a hint what triggers it... – ain Jul 31 '13 at 09:03
  • It is unclear what has been happend exactly. Window contains Tree object (navigation) and Embedded browser (display infos). After several clicks or on form closing user got an error. I can't reproduce this problem, information is based on bugreports and user requests – Юрий Тимофеев Jul 31 '13 at 09:09
  • Does any of your code (for example in event handlers) get executed in this infinite recursion? If so, can we see that code. – David Heffernan Jul 31 '13 at 09:12
  • Sorry, call stack section size is limited, so I has no info about source of infinite loop. But it seems like inadequate reaction on normal situation. – Юрий Тимофеев Jul 31 '13 at 09:12
  • What eventhandlers do you implement for the VT? The error on close suggest to look at `OnFreeNode`, click that some paint or focus change event might mess up something... – ain Jul 31 '13 at 09:15
  • No one line of my code is'not executed in this loop. The whole "call stack" is like this: GetNodeHeight GetNodeHeight GetDisplayRect GetDisplayRect InvalidateToBottom InvalidateToBottom SetNodeHeight SetNodeHeight with some small variations. Can i attach Eurekalog file? – Юрий Тимофеев Jul 31 '13 at 09:16
  • Free node? Possible but not in stack. Free node method is very simle: procedure TTrainingForm.TreeFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode); begin SetLength(PTreeData(Tree.GetNodeData(Node))^.Text, 0); end; – Юрий Тимофеев Jul 31 '13 at 09:19
  • What event handlers are attached to this control? – David Heffernan Jul 31 '13 at 09:26
  • In your `OnFreeNode` handler I would check does the `Tree.GetNodeData(Node)` return a valid pointer (`not nil`). – ain Jul 31 '13 at 09:33
  • Event handlers is: TreeChange TreeCollapsing TreeFocusChanging TreeFreeNode TreeGetHint **TreeMeasureItem** procedure TTrainingForm.TreeMeasureItem(Sender: TBaseVirtualTree; TargetCanvas: TCanvas; Node: PVirtualNode; var NodeHeight: Integer); begin inherited; if Sender.MultiLine[Node] then begin try TargetCanvas.Font := Sender.Font; NodeHeight := TVirtualStringTree(Sender).ComputeNodeHeight(TargetCanvas, Node, 0) + 4; except NodeHeight := 24; end; end end; – Юрий Тимофеев Jul 31 '13 at 09:37
  • Sorry, can't format messages – Юрий Тимофеев Jul 31 '13 at 09:39
  • So it turns out that your code is indeed being executed after all. Your `OnMeasureItem` would seem to be the problem. In there you set `NodeHeight`. You should be able to see the recursion now. I've no idea how this control works, but the mechanism for the recursion is at least clear. – David Heffernan Jul 31 '13 at 09:39
  • Ok, just updated the question text. Shoud I place ComputeNodeHeight into try except block? – Юрий Тимофеев Jul 31 '13 at 09:42
  • I'd explored the code of `ComputeNodeHeight`. It does'not call any of this MeasureItemHeight. It only use `Windows.DrawTextW` with `DT_CALCRECT` flag to determine the height of text cell. So, I still unable to fix problem. – Юрий Тимофеев Jul 31 '13 at 09:59
  • `MeasureItem` fires your `OnMeasureItem` event. You then set the `NodeHeight` var parameter. Then `MeasureItem` presumably sets the `NodeHeight` property, and then you go around again. The question is why you go round again, but you really ought to be able to see the mechanism. After all you have the source code for the control. Next question for you is to work out why `SetNodeHeight` calls `InvalidateToBottom`. – David Heffernan Jul 31 '13 at 10:17
  • Method `TBaseVirtualTree.MeasureItemHeight` call `TreeMeasureItem` then `SetNodeHeight`. `SetNodeHeight` calls `InvalidateToBottom`, if... If statement is like this: `if not (tsValidating in FStates) and FullyVisible[Node] and not IsEffectivelyFiltered[Node] then...`. This is in VirtualTrees.pas, the internals of component code. Run it several times. It's OK in my computer, but... – Юрий Тимофеев Jul 31 '13 at 10:28

1 Answers1

3

In TBaseVirtualTree.SetNodeHeight() the body of the function will only be entered if Node.NodeHeight <> Value. To get into this loop seems only to be possible if you supply for one node a different value each time in your OnMeasureItem event handler. As a first test, use a constant value here and see if the stack overflow disappears. If so, make a debug output for your calculated item heights and carefully check them if they are deterministic for a node. If not, this is the source of the problem. Then reduce the complexity of the code in your OnMeasureItem event handler to find the culprit.

Joachim Marder
  • 782
  • 5
  • 12