【发布时间】:2020-09-17 22:22:29
【问题描述】:
根据文档,TVirtualStringTree 的属性 RootNode 是一个隐藏节点,是所有用户创建的节点的父节点。但是设置了 RootNode 的 Parent。我面临以下问题:
有时,当关闭其中包含 TVirtualStringTree 的表单时,我会收到访问冲突错误。调试,尝试访问组件(虚拟字符串树)的Name属性时发生错误。如果我尝试评估 Name 属性,我会得到“无法访问的值”。深挖问题,发现一个组件的Name属性cannot be changed in runtime:
警告:在运行时更改名称会导致对旧名称的任何引用 名称变得未定义。任何使用旧名称的后续代码 会引发异常。
碰巧我没有在我的代码中更改它。使用 Name 属性中的数据断点,我看到它在 virtualstringtree 循环中发生变化,类似这样(我在示例应用程序中使用的示例代码来测试问题,但生产代码类似):
var
N: PNode;
P: PVirtualNode;
begin
P := tree.FocusedNode;
while Assigned(P) do
begin
N := tree.GetNodeData(P);
P := P.Parent;
end;
GetNodeData 如下:
if (FNodeDataSize <= 0) or (Node = nil) or (Node = FRoot) then
Result := nil
else begin
Result := PByte(@Node.Data) + FTotalInternalDataSize;
Include(Node.States, vsOnFreeNodeCallRequired); // We now need to call OnFreeNode, see bug #323
end;
假设我有以下树:
Node 1
Node 2
Node 3
When the Node 3 is selected, the loop is executed 5 times. 3 在我创建的节点中,然后是 RootNode(隐藏),然后是根的 Parent。只有父根节点的Parent 为nil。在GetNodeData方法中验证是否是根节点,返回nil。但是对于根的这个父节点,因为它不是根,所以输入 else 代码。现在问题来了:
virtualstrintree 的 Name 属性的地址是 $141B9768
根节点的父节点的States属性地址是$141B976A
在 Name 属性中放置数据断点时,此时会发生变化:
Include(Node.States, vsOnFreeNodeCallRequired);
并生成 AV。
我知道我可以更改循环,以检查根节点correctly,但我想了解这一点,所以我可能会修复组件(如果它是组件错误),而不是我的代码。
代码:
type
TNode = record
Text: string;
end;
PNode = ^TNode;
...
procedure TForm1.FormCreate(Sender: TObject);
var
N1, N2, N3: TNode;
TreeNode: PVirtualNode;
begin
N1.Text := 'Node 1';
TreeNode := tree.AddChild(nil, PNode(N1));
N2.Text := 'Node 2';
TreeNode := tree.AddChild(TreeNode, PNode(N2));
N3.Text := 'Node 3';
TreeNode := tree.AddChild(TreeNode, PNode(N3));
end;
在表单中放一个树和一个按钮,我把循环放在按钮的点击中。
【问题讨论】:
-
您谈论一个示例应用程序来测试问题。你能在某处提供它的源代码吗?
-
@fpiette 它只是一个带有树的项目,并在 FormCreate 中创建了 3 个节点。不值得让它可用,重要的是循环本身。使用版本 5.3.0
-
这个想法是我可以复制您的代码来重现您的问题,而无需自己做太多工作。所以是的,值得让它可用。由于它很短 - 正如你所说 - 你可以编辑你的问题并添加该代码。
-
@fpiette 对不起,我编辑了帖子