Announcement

Collapse
No announcement yet.

Delphi-Bug bei Interface-Properties ?

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Delphi-Bug bei Interface-Properties ?

    Folgender Fall:

    Eine COM-Klasse X beinhaltet ein Property Y zu einer anderen COM-Klasse, die die Methode Z implementiert.
    Wird jetzt x.y.z aufgerufen, so wird der Referenzzähler von y automatisch hochgezählt, aber nicht mehr dekrementiert.
    Wird jedoch x.y in einer Variablen zwischengeparkt, anschließend davon die Methode z aufgerufen und dann der temporären Variablen Nil zugewiesen, so ist der Referenzzähler von y am Ende wieder korrekt.

    Somit liegt der Schluß nahe, daß Delphi den Zähler bei der Funktions-Rückgabe erhöht, aber nicht merkt, daß der Rückgabewert nicht ausgewertet wird und den Zähler nie wieder erniedrigt. Scheint ein Bug zu sein, weiß jemand darüber mehr ?

    Gruß Ralf

  • #2
    Hallo,

    wie sieht eine Mini-Bespiel aus, mit dem sich dieser Effekt jederzeit reproduzieren lässt

    Comment


    • #3
      Hallo Herr Kosch.

      Nachfolgend ein Stück Quellcode mit einigen Erläuterungen.
      Bei Bedarf existieren natürlich auch die Applikation und die ActiveX-Bibliothek.

      <pre>
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      // RefObj1 beinhaltet die Property RefObj2, die mit CoCreate in der Initialize-Methode von RefObj1 erzeugt wird.
      // Weiterhin beinhaltet RefObj1 eine Methode zum Erzeugen von RefObj3, das die erzeugende Instanz von RefObj1 speichert.
      // Jetzt wird folgendes gemacht:
      // RefObj1.RefObj3Create wird aufgerufen, um eine Instanz von RefObj3 zu erhalten.
      // Jetzt steht der RefCounter von RefObj1 auf 2, da sich RefObj3 das aufrufende RefObj1 merkt.
      // Anschließend wird die Methode ShowMessage von RefObj3.RefObj1.RefObj2 aufgerufen, nun steht der Zähler von RefObj1 auf 3
      // Setzt man nun RefObj3 auf Nil, wird der Zähler um eins dekrementiert, was auch noch ok ist, aber er steht danach immer noch
      // auf 2. Somit hat man eine verlorene Referenz.
      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

      function GetInterfaceRefCounter (const aInterface : IUnknown) : integer;
      begin
      Result := aInterface._AddRef;
      Result := Result - 1;
      aInterface._Release;
      end;

      procedure ShowInterfaceRefCounter (s : string; const aInterface : IUnknown);
      begin
      ShowMessage (s + '.RefCounter = ' + IntToStr (GetInterfaceRefCounter (aInterface)));
      end;

      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

      procedure TForm1.Button1Click(Sender: TObject);
      // Button1.Click erzeugt eine verlorene Referenz
      var RefObj1 : IRefObj1;
      RefObj3 : IRefObj3;
      begin
      RefObj1 := CoRefObj1.Create; // erzeugt das Basisobjekt
      RefObj3 := RefObj1.RefObj3Create; // erzeugt eine Instanz von Obj3
      RefObj3.RefObj1.RefObj2.ShowMessage (''); // hier passierts !!!
      RefObj3 := Nil;
      ShowInterfaceRefCounter ('', RefObj1); // der Refcounter von Obj1 steht jetzt auf 2 !
      RefObj1 := Nil;
      end;

      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

      procedure TForm1.Button2Click(Sender: TObject);
      // Button2.Click parkt die referenz zwischen und alles ist ok
      var RefObj1 : IRefObj1;
      RefObj3 : IRefObj3;
      RefObj1Ok : IRefObj1;
      begin
      RefObj1 := CoRefObj1.Create; // erzeugt das Basisobjekt
      RefObj3 := RefObj1.RefObj3Create; // erzeugt eine Instanz von Obj3
      RefObj1Ok := RefObj3.RefObj1; // jetzt wird die Referenz zwischengeparkt
      RefObj1Ok.RefObj2.ShowMessage (''); // hier passierts !!!
      RefObj1Ok := Nil; // jetzt wird die Referenz wieder freigegeben
      RefObj3 := Nil;
      ShowInterfaceRefCounter ('', RefObj1); // der Refcounter von Obj1 steht jetzt auf 1 !
      RefObj1 := Nil;
      end;

      ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

      </pre>

      Gruß Ral

      Comment


      • #4
        Wie sieht die Implementierung von <i>RefObj3.RefObj1</i> aus und wie wird der von <i>RefObj1</i> zurückgelieferte Interface-Zeiger in RefObj3 zwischengespeichert?

        Comment


        • #5
          Hallo Herr Kosch.

          Die Implementierung von RefObj1 sieht folgendermaßen aus:

          TRefObj1.RefObj3Create erzeugt ein RefObj3-Objekt und speichert darin ein Interface von sich selbst in RefObj3.

          <pre>
          type
          TRefObj1 = class(TAutoObject, IRefObj1)
          private
          FRefObj2 : IRefObj2;
          protected
          function Get_RefObj2: IRefObj2; safecall;
          function RefObj3Create: IRefObj3; safecall;
          public
          procedure Initialize; override;
          destructor Destroy; override;
          end;

          implementation

          uses ComServ;

          procedure TRefObj1.Initialize;
          begin
          inherited Initialize;
          FRefObj2 := CoRefObj2.Create;
          end;

          destructor TRefObj1.Destroy;
          begin
          FRefObj2 := Nil;
          inherited Destroy;
          end;

          function TRefObj1.Get_RefObj2: IRefObj2;
          begin
          Result := FRefObj2;
          end;

          function TRefObj1.RefObj3Create: IRefObj3;
          begin
          Result := CoRefObj3.Create;
          Result.RefObj1 := Self;
          end;

          </pre>

          Die Implementierung von RefObj3 sieht folgendermaßen aus:

          <pre>
          type
          TRefObj3 = class(TAutoObject, IRefObj3)
          private
          FRefObj1 : IRefObj1;
          protected
          function Get_RefObj1: IRefObj1; safecall;
          procedure Set_RefObj1(const Value: IRefObj1); safecall;

          end;

          implementation

          uses ComServ;

          function TRefObj3.Get_RefObj1: IRefObj1;
          begin
          Result := FRefObj1;
          end;

          procedure TRefObj3.Set_RefObj1(const Value: IRefObj1);
          begin
          FRefObj1 := value;
          end;
          </pre>

          Gruß Ral

          Comment


          • #6
            Hallo,

            da in der Implementierung für die Variablen immer nur der Interface-Typ verwendet wird, muss es in der Tat ein Bug von Delphi sein.

            Nur dann, wenn man den Klassen- und Interface-Typ mixt und auf den Delphi-internen Mechanismus vertraut, hätte man sich das Problem selbst zuschreiben müssen

            Comment

            Working...
            X