44

我编写了一组通过发布的接口属性相互链接的组件。它们已注册并安装在设计包中。

使用已发布的接口属性在 Delphi 中并不常见,因此,不出所料,似乎效果不佳。

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

与指向另一个表单上的组件的对象链接不同,IDE 似乎无法识别界面链接。我的意思最好用一个例子来描述,当你在 IDE 中打开了 2 个表单,并且在它们上的组件之间有链接,然后尝试以文本形式切换到表单视图 (Alt+F12) 会导致 IDE 正确地抱怨:

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

但是,如果属性是一个接口,那么这不会发生,而是会发生链接被切断(这是使用通知机制清除引用时的最佳情况,否则您将得到一个无效的指针)

另一个问题,可能是由于相同的错误,当您在 IDE 中打开一个项目时,重新打开表单的顺序是未定义的,因此 IDE 可以尝试打开一个包含组件的表单,该组件具有指向组件的接口链接另一种形式,但尚未重新创建另一种形式。因此,这有效地导致了 AV 或断开的链接。

回到 90 年代,当我使用时DatasetsDatasources我记得表单之间的链接消失的类似问题,所以这有点相似。

作为临时解决方法,我添加了重复的已发布属性,对于每个 Interface 属性,我添加了另一个声明为TComponent. 这让 Delphi 意识到表单之间存在联系,但至少可以说是一个丑陋的解决方法。

所以我想知道是否有什么办法可以解决这个问题?这是一个 IDE 错误,可能无法直接修复,但也许我可以覆盖某些内容或以其他方式连接到流机制以更有效地解决此错误。

我从来没有深入研究过流媒体机制,但我怀疑 Fixup 机制应该可以解决这个问题。有一个csFixups TComponentState所以我希望有一个解决方法是可能的。

编辑:使用D2007

更新:

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

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

我将问题缩小到汇编程序级别,这有点超出我的深度。

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我不知道。

4

1 回答 1

2

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

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

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

于 2013-05-08T08:44:15.603 回答