发布的接口属性错误和解决方法

我写了一组通过发布的接口属性相互链接的组件。 他们被注册并安装在一个devise包中。

在Delphi中使用已发布的接口属性并不常见,因此毫不奇怪,似乎并不能很好地工作。

当组件驻留在同一个表单上时它工作正常,但是不同表单上的组件之间的接口属性链接会导致问题。

与另一个表单上组件的对象链接不同,接口链接似乎无法被IDE识别。 我的意思是最好用一个例子来描述,当你在IDE中打开两个窗体,并在它们之间有组件之间的链接,然后尝试切换到窗体视图为文本(Alt + F12)将导致IDE正确地抱怨:

Module 'UnitXXX.pas' has open descendents or linked modules. Cannot close.

但是,如果属性是一个接口,那么这不会发生,反而会发生链接被切断(当使用Notification机制来清除引用时,这是最好的情况,否则你会留下一个无效的指针)

另一个问题,可能是由于相同的错误造成的结果是,当您在IDE中打开一个项目时,表单将被重新打开的顺序是未定义的,因此IDE可以尝试打开一个窗体,其中包含组件的接口链接另一种forms,但其他forms尚未重新创build。 所以这有效地导致AV或切断的链接。

早在90年代,当我使用DatasetsDatasources我记得类似的问题与表单之间的链接消失,所以这有些类似。

作为临时替代方法,我添加了重复的发布属性,对于每个接口属性,我添加了另一个声明为TComponent 。 这使delphi意识到forms之间有联系,但至less可以说是一个丑陋的解决方法。

所以我想知道我能做些什么来解决这个问题? 这是一个IDE错误,可能无法直接解决,但也许我可以重写某些东西或以其他方式挂接到stream式机制,以更有效地解决此问题。

我从来没有深入到stream式机制,但我怀疑Fixup机制应该处理这个问题。 有一个csFixups所以我希望有一个解决方法是可能的。

编辑:使用D2007

更新:

新的更新可重复的例子上传到http://www.filedropper.com/fixupbugproject2

添加了property ComponentReference: TComponent以便于比较和跟踪接口与组件stream。

我把问题缩小到汇编层面,这有点超出我的深度。

在它调用的classes单位过程GlobalFixupReferences中:

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

正如你从注释中看到的,我发现避免exception的唯一方法是将结果保留为未赋值,但由于GetOrdProp <> 0 ,导致GlobalFixupReferences比较失败,从而导致function中断。

更深入地追踪exception的更确切位置

procedure _IntfCopy(var Dest: IInterface; const Source: IInterface);system单元中

这条线特别引起read of address 0x80000000read of address 0x80000000

 { Now we're into the less common cases. } @@NilSource: MOV ECX, [EAX] // get current value 

那么,为什么MOV失败, ECXEAX有什么问题我不知道。

总而言之,问题只发生在具有getter方法的发布接口属性上,并且该属性指向另一个表单/模块上的组件(并且该表单/模块尚未重新创build)。 在这种情况下,恢复formsDFM导致AV。

我很确定这个错误是在GetOrdProp的ASM代码中,但是我无法修复,所以最简单的解决方法是使用Field而不是getter方法,直接在属性中读取。 幸运的是,目前我的情况已经够好了。

或者,可以将属性声明为TComponent而不是接口,然后编写TComponentProperty后代,重写ComponentMayBeSetTo以过滤不支持所需接口的组件。 当然,使用RegisterPropertyEditor进行RegisterPropertyEditor