我编写了一组通过已发布的接口(interface)属性相互链接的组件。它们已注册并安装在设计包中。
使用已发布的接口(interface)属性在Delphi中并不常见,因此,毫不奇怪,它似乎工作得并不好。
当组件位于同一表单上时,它可以正常工作,但是不同表单上的组件之间的接口(interface)属性链接会导致问题。
与对象链接到另一种形式的组件不同,接口(interface)链接似乎无法被IDE识别。我的意思是用一个示例来最好地描述,当您在IDE中打开2个表单,并且在它们之间具有链接,然后尝试以文本形式切换到表单 View (Alt + F12)时,IDE会正确地抱怨:Module 'UnitXXX.pas' has open descendents or linked modules. Cannot close.
但是,如果该属性是接口(interface),则不会发生这种情况,而是会断开链接(这是使用通知机制清除引用的最佳情况,否则将留下无效的指针)
另一个问题(可能是同一错误的结果)是,当您在IDE中打开项目时,未重新打开表单的顺序是不确定的,因此IDE可以尝试打开一个包含包含与组件的接口(interface)链接的组件的表单。另一种形式,但尚未重新创建该其他形式。因此,这有效地导致了AV或断开连接。
上世纪90年代,当我使用Datasets
和Datasources
时,我还记得表单之间的链接消失的类似问题,所以这有点相似。
作为临时解决方法,我添加了重复的已发布属性,对于每个Interface属性,我添加了另一个声明为TComponent
的属性。这使Delphi意识到表单之间存在联系,但至少可以说是一个丑陋的解决方法。
所以我想知道是否有什么我可以解决的问题?这是一个IDE错误,可能无法直接修复,但是也许我可以重写某些内容,或者以其他方式挂接到流机制中,以更有效地解决此错误。
我还没有深入研究流传输机制,但是我怀疑Fixup机制应该可以解决这个问题。有一个csFixups
TComponentState
,所以我希望有一种解决方法。
编辑:使用 D2007 。
更新:
新的更新的可复制示例已上载到http://www.filedropper.com/fixupbugproject2
添加了property ComponentReference: TComponent
,以便轻松比较和跟踪接口(interface)与组件流。
我将问题缩小到汇编程序级别,这有点超出我的理解范围了。
在GlobalFixupReferences
单元中的classes
过程中,它调用:(GetOrdProp(FInstance, FPropInfo) <> 0)
最终执行:
function TInterfacedComponent.GetInterfaceReference: IInterface;
begin
// uncomment the code bellow to avoid exception
{ if (csLoading in ComponentState) and (FInterfaceReference = nil) then
// leave result unassigned to avoid exception
else
}
result := FInterfaceReference; // <----- Exception happens here
end;
从评论中可以看到,我发现避免异常的唯一方法是不分配结果,但这会破坏功能,因为
GlobalFixupReferences
会导致上面GetOrdProp <> 0
中的比较失败,这会断开链接。越深入地追踪异常的更精确位置
procedure _IntfCopy(var Dest: IInterface; const Source: IInterface);
单位中的system
该行特别引发了
read of address 0x80000000
{ Now we're into the less common cases. }
@@NilSource:
MOV ECX, [EAX] // get current value
所以,为什么
MOV
失败,以及ECX
或EAX
怎么了,我不知道。 最佳答案
总而言之,问题仅在具有getter方法的已发布接口(interface)属性中发生,并且该属性指向另一个表单/模块上的组件(并且尚未重新创建该表单/模块)。在这种情况下,恢复表格DFM会导致AV。
我非常确定该错误位于GetOrdProp
的ASM代码中,但超出了我的修复范围,因此
最简单的解决方法是使用Field而不是getter方法,并直接在属性中读取它。幸运的是,这对于我目前的情况已经足够了。
另外,您可以将属性声明为TComponent
而不是interface,然后编写TComponentProperty
子孙,覆盖ComponentMayBeSetTo
以过滤不支持所需接口(interface)的组件。并且当然使用RegisterPropertyEditor
注册它