【问题标题】:Delphi objects, NIL objects and InterfacesDelphi 对象、NIL 对象和接口
【发布时间】:2011-04-10 08:57:09
【问题描述】:

我正在寻找有关如何在使用 Delphi VCL 中的 MS XML 包装器的应用程序中调试崩溃的提示。我怀疑内存损坏,或者对象和接口之间发生了某种晦涩难懂的事情,例如引用计数错误或堆损坏。实际上,问题是:如何调试这样的崩溃?

此特定代码在内部大量使用并扩展了基本 XmlIntf 接口 (IXMLNode)。 ISomethingCustom 是一个扩展 IXMLNode 的接口。问题发生在我们在某个递归函数中崩溃的地方,该函数传递了一个 ISomethingCustom,它也是(或在接口方面也支持)IXMLNode。

   boolean UtilityFunction( aNode: ISomethingCustom ):Boolean;
   begin
      if not Assigned(aNode) then exit; // this works. great.
      if not Assigned(aNode.ParentNode) then exit; // this DOES NOT WORK.
     // code that blows up if aNode.ParentNode is not assigned.
   end;

情况是 aNode 也是 IXMLNode,并且 IXMLNode.ParentNode 值被赋值(不是 nil),但它指向一个可能已被释放、销毁或以某种方式损坏的 COM 对象。我试图弄清楚当接口指针看起来有效时发生了什么,但它背后的对象已经以某种方式被核了。

检查 Assigned(aNode.ParentNode) 会返回 TRUE,即使您尝试在调试器中进行强制转换(仅在运行时,而不是在代码中),如下所示:

  1. 检查/评估节点
  2. 检查/评估 TInterfacedObject(aNode).ClassName (至少在 Delphi 2010 中有效!)
  3. 现在投射 TWhateverClassNameYouGotBefore(aNode)。
  4. 在调试器中,我现在看到这是 NIL。这可能意味着 神奇的“铸造界面回到 对象”的新功能 delphi 2010,失败了。

我相信我正在尝试调试由于引用计数问题而导致堆损坏或堆上的 COM 对象损坏的问题。

我真的认为没有人应该出现这样的情况:接口看起来有效,但下面的对象已被删除。我真的很想知道该怎么做,发生了什么。

【问题讨论】:

  • 检查 Assigned(aNode.ParentNode) 返回 TRUE,即使 TNode(aNode).ParentNode 实际上是 NIL。
  • 一定是做坏事了。在我注意到某些东西被破坏之前不会故意进行任何转换,然后我在调试器的评估表达式中进行转换,只是为了看看我是否能看到关于接口背后的任何内容。 :-)

标签: delphi com interface null


【解决方案1】:

虽然您没有在您的代码 中显示它,但您的 cmets 似乎表明您正在将接口变量类型转换为类类型。这是不允许的。我已经描述了原因:

接口引用和对象引用并不指向相同的东西。因此,当编译器认为你有另一个方法时调用一个方法会产生意想不到的结果。你很倒霉,因为代码继续运行而不是因为访问冲突而崩溃,这更能表明你做错了什么。

如果您有一个 Delphi 实现的接口并且该接口没有提供自己的函数来显示底层对象,我上面的文章最后建议您使用 JCL 中的 JclSysUtils​.GetImplementorOfInterface 函数。

【讨论】:

  • 将接口指针类型转换回其实现类指针是 Delphi 2010 或 XE 中的一项新功能,我忘记是哪一个介绍了它。
  • 我只是转换回对象,因为我注意到这样的转换导致 NIL 值,并且觉得这很好奇。在运行时,我实际上并没有做这样的转换,除了调试代码,我在发现你说的内容后迅速删除了它。它仍然崩溃,这意味着我要么堆损坏,要么出现其他问题。
  • 我几乎完全重写了我的问题,我希望它能让我的情况更加清楚。你上面的“指针”让我明白了一些我以前不明白的东西,这很好。鉴于我对情况的完全误解,我想知道这个问题是否可以挽救,或者我是否应该在我知道发生了什么后再次重写它。
【解决方案2】:

猜想:你有没有试过把aNode.ParentNode放在一个局部变量中,并在Utilityfunction的其余部分使用它:

   function UtilityFunction(aNode: ISomethingCustom): Boolean;
   var
     lParentNode: INode;
   begin
      if not Assigned(aNode) then exit; // this works. great.
      lParentNode := aNode.ParentNode;
      if not Assigned(lParentNode) then exit;
     // code that uses lParentNode.
   end;

【讨论】:

  • 它似乎是一个有效的非 nil 接口指针,但是当转换回像 TXMLNode(aNode) 这样的对象时,你得到 nil。有时,这在调试时有效,但在代码中无效(代码看不到 nil,但调试器会看到)。毛茸茸的。
  • 大多数 Delphi 版本不允许您将接口指针类型转换回对象指针。这是一个仅在 Delphi 2010 或 XE 中可用的新功能,我忘记了它是由哪个引入的。在此之前,从接口指针获取对象指针的唯一方法是让接口实现一个返回实现类的 Self 指针的方法。
  • 我刚刚了解到这一点。除了我在调试器中进行转换并发现您可以在调试器表达式评估器中找出它首先是哪个类时,我们不会故意进行任何此类转换。
【解决方案3】:

我的建议是确保在Assigned(aNode.ParentNode) 中实际调用了 ParentNode 函数。在 Delphi 中有一些令人讨厌的极端情况,其中没有调用没有参数的过程/函数,而是在省略括号时获取它的引用。

尝试将其更改为Assigned(Anode.ParentNode())(应该与弗朗索瓦建议的效果相同)。

【讨论】:

    猜你喜欢
    • 2011-06-16
    • 2011-06-27
    • 1970-01-01
    • 1970-01-01
    • 2015-07-03
    • 1970-01-01
    • 1970-01-01
    • 2017-11-14
    • 2011-05-07
    相关资源
    最近更新 更多