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

44
我编写了一组组件,这些组件通过发布的接口属性相互链接。它们在设计包中注册和安装。在 Delphi 中使用发布的接口属性并不常见,因此不出所料,似乎并不起作用。当组件位于同一窗体上时,它可以正常工作,但是在不同窗体之间的接口属性链接会导致问题。与在另一个窗体上链接到组件的对象链接不同,IDE 似乎无法识别界面链接。我的意思最好用一个例子来描述,当你在 IDE 中打开 2 个窗体,并在它们之间链接组件时,尝试切换到窗体视图作为文本(Alt+F12)将导致 IDE 正确地抱怨:Module 'UnitXXX.pas' has open descendents or linked modules. Cannot close. ,但如果属性是一个接口,则不会发生这种情况,相反,链接将被切断(如果使用通知机制来清除引用,则这是最佳情况,否则您将得到一个无效指针)。另一个问题,很可能是由于同一个 bug 的后果,当你在 IDE 中打开项目时,窗体重新打开的顺序是未定义的,因此 IDE 可能会尝试打开包含链接到另一个窗体上组件的窗体,但该窗体尚未重新创建。因此,这实际上导致 AV 或已切断的链接。DatasetDatasource 在 90 年代使用时,我记得类似问题的链接会消失,因此这有些相似。作为临时解决办法,我添加了重复的发布属性,对于每个接口属性,我添加了另一个声明为 TComponent 的属性。这使 Delphi 意识到窗体之间存在链接,但至少是一个丑陋的解决办法。因此,我想知道是否有什么方法可以解决这个问题?这是一个 IDE bug,很可能无法直接修复,但也许我可以覆盖某些东西或以其他方式钩入流媒体机制,以更有效地解决这个 bug。我从来没有深入研究过流媒体机制,但我怀疑 Fixup 机制应该处理这个问题。有一个 csFixups TComponentState,所以我希望有可能进行一些解决办法。更新:在 http://www.filedropper.com/fixupbugproject2 上上传了新的可重现示例。添加了 property ComponentReference: TComponent,因此可以轻松比较和跟踪接口与组件流。我把问题缩小到了汇编级别,这有点超出我的深度。在 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;

正如您从评论中看到的那样,我发现避免抛出异常的唯一方法是不给结果赋值,但这破坏了功能性,因为在 GlobalFixupReferences 中上面的比较失败了,由于 GetOrdProp <> 0,这会断开链接。

追踪更深入的异常位置在

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

特别是这一行引发了一个 read of address 0x80000000 异常

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

所以,为什么MOV失败了,ECXEAX出了什么问题我不知道。


5
这是一个有趣的问题。它超出了我的个人经验范围。我猜想如果你有一个演示项目,那会对任何初学者调查员有所帮助。 - David Heffernan
1
@DavidHeffernan,这个问题很容易复现,你只需要一个具有IInterface类型发布属性的TComponent后代。注册它,安装包并将其拖放到两个空白表单中的每一个上即可。 话虽如此,我可以自己做,让你们玩一下... - user219760
1
我认为如果你这样做会有所帮助。降低我们的门槛。 - David Heffernan
1
显然,GetOrdProp不喜欢带有getter函数的属性。如果你将property InterfaceReference ... read GetInterfaceReference更改为property InterfaceReference ... read FInterfaceReference,AV就会消失。 - Jens Mühlenhoff
请参考 https://dev59.com/cVjUa4cB1Zd3GeqPNQe-,这可能会提供一些相关的见解。 - Thomas W
显示剩余4条评论
1个回答

2
总结一下,问题只会出现在已发布的接口属性上,该属性具有getter方法,并且该属性指向另一个表单/模块上的组件(而该表单/模块尚未重新创建)。在这种情况下,恢复表单DFM会导致AV错误。
我很确定错误在GetOrdProp的ASM代码中,但我无法修复它,因此最简单的解决方法是使用字段代替getter方法,并直接在属性中读取它。幸运的是,目前对我的情况来说足够好了。
另外,您可以将属性声明为TComponent,然后编写TComponentProperty的子类,重写ComponentMayBeSetTo以过滤不支持所需接口的组件。当然,还要使用RegisterPropertyEditor进行注册。

这个有没有质控记录? - David
1
这只会让问题持续存在。如果他们不知道问题所在,你就不能抱怨它没有被解决。如果他们知道了问题,你就可以抱怨 :) (我并不是在指责你抱怨,但你明白我的意思吧。) - David

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接