AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Data-Pointer in Interfacevariable casten

Ein Thema von RedOne · begonnen am 14. Jul 2010 · letzter Beitrag vom 16. Jul 2010
Antwort Antwort
Seite 2 von 2     12   
brechi

Registriert seit: 30. Jan 2004
823 Beiträge
 
#11

AW: Data-Pointer in Interfacevariable casten

  Alt 15. Jul 2010, 19:51
Also ich hab das auch mit Delphi2007 getestet. Das AddRef brauchst du weil die InterfaceListe zerstört wird aber du immernoch über Data auf das Object zugreifen willst.

So gehts:

Delphi-Quellcode:
type
  IDataObject = interface(IInterface)
    ['{C94DCAC7-CA42-45D3-95D7-9BC321BA2E2A}']
    procedure Delete;
  end;

  IAccountData = interface(IInterface)['{DA626905-CB35-4474-B353-9F8E973709AA}']
    function GetId: Integer;
    procedure SetId(_Id: Integer);
    property ID: Integer read Getid write SetId;
  end;

  TAccountData = class(TInterfacedObject, IAccountData)
  protected
    FID: Integer;
    function Getid: Integer;
    procedure SetId(_Id: Integer);
  public
    destructor Destroy; override;
  end;

  TAccountDataExtend = class(TAccountData, IDataObject)
  public
    procedure Delete;
  end;

procedure TForm9.ListViewMainClick(Sender: TObject);
var
  obj: IInterface;
  obj3: IDataObject;
begin
  inherited;
  if ListViewMain.ItemIndex >= 0 then begin
    obj := IInterface(ListViewMain.Selected.Data);
    if Succeeded(obj.QueryInterface(IDataObject, obj3)) then begin
      obj3.Delete; //Funktion siehe unten
    end;
  end;
end;

procedure TForm9.ListViewMainDeletion(Sender: TObject; Item: TListItem);
begin
  IInterface(Item.Data)._Release;
end;

procedure TForm9.Refresh;
var
  tmp: IAccountData;
  i: Integer;
  item: TListItem;
  x: TInterfaceList;
begin
  //Vorgängig Listview aufräumen, Objekte zerstören, Interface Releasen

  x := TInterfaceList.Create;
  try
    GetAllAccounts(x); //Siehe Funktion unten
    for i := 0 to x.Count - 1 do begin
      if Succeeded(x[i].QueryInterface(IAccountData, tmp)) then begin
        item := ListViewMain.Items.Add;
        item.Caption := IntToStr(tmp.ID); //Zeigt korrekte ID an
        item.Data := Pointer(tmp);
      end;
    end;
  finally
    FreeAndNil(x); //Ich nehme an die InterfaceList zerstört die Objekte nicht
  end;
end {LoadListView};

procedure TForm9.Button1Click(Sender: TObject);
begin
  Refresh;
end;

procedure TForm9.GetAllAccounts(_o: TInterfaceList);
var
  tmp: IAccountData; //Oder IInterface?
  i: integer;
begin
  for i := 0 to 4 do begin
    tmp := TAccountDataExtend.Create;
    tmp.ID := i;
    tmp._AddRef;
    _o.Add(tmp);
  end;
end {GetAllAccounts};

//----------------- TAccountDataExtend

procedure TAccountDataExtend.Delete;
begin
  ShowMessage(IntToStr(FID));
end {Delete};

{ TAccountData }

destructor TAccountData.Destroy;
begin
  MessageBox(0,'del',nil,0);
  inherited;
end;

function TAccountData.Getid: Integer;
begin
  Result := Fid;
end;

procedure TAccountData.SetId(_Id: Integer);
begin
  Fid := _id;
end;
hab auch mal nen destructor eingebaut, damit du siehst wann das object zerstört wird (ohne _addref schon beim zerstören der InterfaceList). Und damit du kein Speicherleck hast musst du auch wieder _Release aufrufen.

Im grunde brignt das das nte viel bei dem Beispiel weil du ne Sonderbehandlung bei Data brauchst. Dann kannst auch auf interfaces verzichten und gleich selbst freigeben (es sei denn es wird ein externes Modul wo du mit interfaces drauf zugreifen willst).
  Mit Zitat antworten Zitat
brechi

Registriert seit: 30. Jan 2004
823 Beiträge
 
#12

AW: Data-Pointer in Interfacevariable casten

  Alt 15. Jul 2010, 20:08
Oder du schreibst dir deine eigene TListView die alles für dich übernimmt:

Delphi-Quellcode:
unit Unit9;

interface

uses
  Windows,
  Messages,
  SysUtils,
  Variants,
  Classes,
  Graphics,
  Controls,
  Forms,
  Dialogs,
  ComCtrls,
  StdCtrls;

type
  TListView = class(comCtrls.TListView)
  private
    FOnDeletion: TLVDeletedEvent;
    procedure SetOnDeletion(const Value: TLVDeletedEvent);
    function GetOnDeletion: TLVDeletedEvent;
  protected
    procedure InternOnDeletion(Sender: TObject; Item: TListItem);
  public
    constructor Create(AOwner: TComponent); override;
  published

    procedure AddItem(Item: String; AObject: IInterface); reintroduce;
    property OnDeletion: TLVDeletedEvent read GetOnDeletion write SetOnDeletion;
  end;

  TForm9 = class(TForm)
    ListViewMain: TListView;
    Button1: TButton;
    procedure ListViewMainClick(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    procedure Refresh;
    procedure GetAllAccounts(_o: TInterfaceList);
  public
    { Public declarations }
  end;

var
  Form9: TForm9;

implementation

{$R *.dfm}

type
  IDataObject = interface(IInterface)
    ['{C94DCAC7-CA42-45D3-95D7-9BC321BA2E2A}']
    procedure Delete;
  end;

  IAccountData = interface(IInterface)['{DA626905-CB35-4474-B353-9F8E973709AA}']
    function GetId: Integer;
    procedure SetId(_Id: Integer);
    property ID: Integer read Getid write SetId;
  end;

  TAccountData = class(TInterfacedObject, IAccountData)
  protected
    FID: Integer;
    function Getid: Integer;
    procedure SetId(_Id: Integer);
  public
    destructor Destroy; override;
  end;

  TAccountDataExtend = class(TAccountData, IDataObject)
  public
    procedure Delete;
  end;

procedure TForm9.ListViewMainClick(Sender: TObject);
var
  obj: IInterface;
  obj3: IDataObject;
begin
  inherited;
  if ListViewMain.ItemIndex >= 0 then begin
    obj := IInterface(ListViewMain.Selected.Data);
    if Succeeded(obj.QueryInterface(IDataObject, obj3)) then begin
      obj3.Delete; //Funktion siehe unten
    end;
  end;
end;

procedure TForm9.Refresh;
var
  tmp: IAccountData;
  i: Integer;
  x: TInterfaceList;
begin
  //Vorgängig Listview aufräumen, Objekte zerstören, Interface Releasen

  x := TInterfaceList.Create;
  try
    GetAllAccounts(x); //Siehe Funktion unten
    for i := 0 to x.Count - 1 do begin
      if Succeeded(x[i].QueryInterface(IAccountData, tmp)) then begin
        ListViewMain.AddItem(IntToStr(tmp.ID), tmp);
      end;
    end;
  finally
    FreeAndNil(x); //Ich nehme an die InterfaceList zerstört die Objekte nicht
  end;
end {LoadListView};

procedure TForm9.Button1Click(Sender: TObject);
begin
  Refresh;
end;

procedure TForm9.GetAllAccounts(_o: TInterfaceList);
var
  tmp: IAccountData; //Oder IInterface?
  i: integer;
begin
  for i := 0 to 4 do begin
    tmp := TAccountDataExtend.Create;
    tmp.ID := i;
    _o.Add(tmp);
  end;
end {GetAllAccounts};

//----------------- TAccountDataExtend

procedure TAccountDataExtend.Delete;
begin
  ShowMessage(IntToStr(FID));
end {Delete};

{ TAccountData }

destructor TAccountData.Destroy;
begin
  MessageBox(0,'del',nil,0);
  inherited;
end;

function TAccountData.Getid: Integer;
begin
  Result := Fid;
end;

procedure TAccountData.SetId(_Id: Integer);
begin
  Fid := _id;
end;


{ TListView }

procedure TListView.AddItem(Item: String; AObject: IInterface);
begin
  AObject._AddRef;
  inherited AddItem(Item, Pointer(AObject))
end;

procedure TListView.SetOnDeletion(const Value: TLVDeletedEvent);
begin
  FOnDeletion := Value;
end;

constructor TListView.Create(AOwner: TComponent);
begin
  inherited;
  inherited OnDeletion := InternOnDeletion;
end;

function TListView.GetOnDeletion: TLVDeletedEvent;
begin
  Result := FOnDeletion;
end;

procedure TListView.InternOnDeletion(Sender: TObject; Item: TListItem);
begin
  if Assigned(item.Data) then
    IInterface(item.Data)._Release;
end;

end.
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#13

AW: Data-Pointer in Interfacevariable casten

  Alt 15. Jul 2010, 20:13
In GetAllAccounts hat tmp._AddRef; nichts zu suchen, denn die Interfacelist arbeites ja korreckt und das Objekt wird da auch nie als Objekt verwendet.

Das Problem mit der Referenzzählung liegt an einer ganz anderen Stelle.
Und zwar bei item.Data := Pointer(tmp); , denn da wird eine Instanz des Interfaces gespeichert, aber ohne Berücksichtigung der Referenzzählung.
Nun gibt die InterfaceList natürlich beim FreeAndNil(x) ihre Referenzen frei und da es sonst keine registrierten RTeferenzen gibt, würden da die Interfaces/Objekte natürlich freigegeben.

Außerdem ist der Cast IInterface(ListViewMain.Selected.Data); falsch, denn in .Data steckt ein IAccountData und kein IInterfce.
Beide Interfaces (IAccountData und IInterfce) implementieren zum Glück das selbe QueryInterface, so daß es hier zufällig nicht zu einem Problem kommt.


Delphi-Quellcode:
type
  IDataObject = interface(IInterface)
    ['{C94DCAC7-CA42-45D3-95D7-9BC321BA2E2A}']
    procedure Delete;
  end;

  IAccountData = interface(IInterface)['{DA626905-CB35-4474-B353-9F8E973709AA}']
    function GetId: Integer;
    procedure SetId(_Id: Integer);
    property ID: Integer read Getid write SetId;
  end;

  TAccountData = class(TInterfacedObject, IAccountData)
  protected
    FID: Integer;
    function Getid: Integer;
    procedure SetId(_Id: Integer);
  public
    destructor Destroy; override;
  end;

  TAccountDataExtend = class(TAccountData, IDataObject)
  public
    procedure Delete;
  end;

procedure TForm9.ListViewMainClick(Sender: TObject);
var
  obj: IAccountData;
  obj3: IDataObject;
begin
  //inherited; // himi: nicht nötig ... oder wurde hier was überschrieben?
  if ListViewMain.ItemIndex >= 0 then begin
    obj := IAccountData(ListViewMain.Selected.Data);
    if Succeeded(obj.QueryInterface(IDataObject, obj3)) then begin
      obj3.Delete; //Funktion siehe unten
    end;
  end;
end;

procedure TForm9.ListViewMainDeletion(Sender: TObject; Item: TListItem);
begin
  // himi: korreckt, nur daß hier nun nicht mehr die
  // fehlerhafte Referenz von .GetAllAccounts freigegeben
  // wird, sondern die aus .Refresh ;)
  IAccountData(Item.Data)._Release;
end;

procedure TForm9.Refresh;
var
  tmp: IAccountData;
  i: Integer;
  item: TListItem;
  x: TInterfaceList;
begin
  // Vorgängig Listview aufräumen, Objekte zerstören, Interface Releasen
  // himi: das sollte diese Zeile erledigen
  ListViewMain.Items.Clear;

  x := TInterfaceList.Create;
  try
    GetAllAccounts(x); //Siehe Funktion unten
    for i := 0 to x.Count - 1 do begin
      if Succeeded(x[i].QueryInterface(IAccountData, tmp)) then begin
        item := ListViewMain.Items.Add;
        item.Caption := IntToStr(tmp.ID); //Zeigt korrekte ID an
        tmp._AddRef; // himi: hier die Referenz für .Data reservieren
        item.Data := Pointer(tmp);
      end;
    end;
  finally
    FreeAndNil(x);
    //Ich nehme an die InterfaceList zerstört die Objekte nicht
    // himi: doch, wenn sie die "einzige" Referenz der
    // Interfaces besitzen würde
  end;
end {LoadListView};

procedure TForm9.Button1Click(Sender: TObject);
begin
  Refresh;
end;

procedure TForm9.GetAllAccounts(_o: TInterfaceList);
var
  tmp: IAccountData; //Oder IInterface?
    // himi: nee, denn sonst könnte man ja nicht auf .ID zugreifen
  i: integer;
begin
  for i := 0 to 4 do begin
    tmp := TAccountDataExtend.Create;
    tmp.ID := i;
    //tmp._AddRef; // himi: wie gesagt ... ist falsch hier
    _o.Add(tmp);
  end;
end {GetAllAccounts};

//----------------- TAccountDataExtend

procedure TAccountDataExtend.Delete;
begin
  ShowMessage(IntToStr(FID));
end {Delete};

{ TAccountData }

destructor TAccountData.Destroy;
begin
  ShowMessage('del ' + IntToStr(FID));
  inherited;
end;

function TAccountData.GetId: Integer;
begin
  Result := FID;
end;

procedure TAccountData.SetId(Id: Integer);
begin
  FID := Id;
end;
$2B or not $2B

Geändert von himitsu (15. Jul 2010 um 20:18 Uhr)
  Mit Zitat antworten Zitat
brechi

Registriert seit: 30. Jan 2004
823 Beiträge
 
#14

AW: Data-Pointer in Interfacevariable casten

  Alt 15. Jul 2010, 20:30
Delphi-Quellcode:
tmp._AddRef; // himi: hier die Referenz für .Data reservieren
        item.Data := Pointer(tmp);
hier ist das Addref wirklich besser aufgehoben (in meinem 2. Post mach ich das direkt über das AddItem)

"Beide Interfaces (IAccountData und IInterfce) implementieren zum Glück das selbe QueryInterface, so daß es hier zufällig nicht zu einem Problem kommt."
Ne ist ja gerade kein Zufall da IAccountData = interface(IInterface).
Gut beim Casten kann man natürlich direkt richtig casten, aber man hat dann ggf. Probleme wenn man unterschiedliche Interfaces in die Liste eingefügt hat, dann ist ein IInterface.QueryInterface schon geeigneter, oder übersehe ich da jetzt was ganz dummes?
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#15

AW: Data-Pointer in Interfacevariable casten

  Alt 15. Jul 2010, 20:52
Ne ist ja gerade kein Zufall da IAccountData = interface(IInterface).
OK, das mit den unterschiedlichen Interfaces in der Liste ist ein gutes Argument.

Aber wenn man danach dann wirklich nochmal das richtige Interface (z.B. via Supports) abfragt, dann isses OK.

Nur das Zufällig meinte ich etwas anders ... Wenn man jetzt z.B. etwas Spezielles von irgendeinem Interface (und nicht nur das überall bekannte Supports) nutzten/aufrufen wöllte, dann wäre es schon wichtig, daß man in den richtigen InterfaceType castet.

Also dann gebe ich mich in diesem Fall geschlagen.

hier ist das Addref wirklich besser aufgehoben
Jupp, ist vorallem für den Fall, daß es zwischen ._AddRef und dem Zuweisen auf den Pointer eine Exception gibt (auch wenn sowas selten wäre und wohl nie vorkommt), aber in soeinem Fall würde, die via _AddRef reservierte Referenz, ja nicht angelegt/genutzt und das Interface würde dann nie freigegeben.
@brechi: ich denke mal du weißt es, aber falls sich mal wer nach dem Grund fragt.
$2B or not $2B
  Mit Zitat antworten Zitat
RedOne

Registriert seit: 2. Jun 2008
71 Beiträge
 
Delphi XE2 Professional
 
#16

AW: Data-Pointer in Interfacevariable casten

  Alt 16. Jul 2010, 09:52
Hallo zusammen

Interessante Anmerkungen, merci.
Habe es soweit umgesetzt. Einziger Nachteil: Ich bin faul
Sehe ich es richtig dass es in Delphi 2007 keinen Weg gibt ein Interfaceobjekt, also mein Objekt im Data direkt in ein Objekt umzuwandeln, in TAccountData umzuwandeln? Ich rufe im OnClickereignis eine abstrakte Methode auf die im abgeleiteten Form implementiert wird. Dieser Methode gebe ich das markierte Objekt aus dem Data mit. Doch dieses sollte ich in mein TAccountData (oder TActionData, je nach dem welches Formular aufgerufen wurde) Objekt umwandeln da die Funktionen die ich brauche für jedes Objekt anders ist und daher nicht im IDataObject implementiert werden können.
Ansonsten muss ich für jede Klasse ein Interface erstellen und für sämtliche Attribute (ist ja nicht nur die ID) eine Get und Set-Methode zu schreiben (ein Property direkt auf die Variable ist im Interface nicht möglich).
Ich hoffe dieses kauderwelsch hat man verstanden

Verständnisfrage: Beim abarbeiten meiner IInterfaceList schreibe ich das Objekt in das Data-Attribut. Jetzt wird doch der Pointer des Objektes dort eingetragen, oder? Von daher sollte ich doch auf das Objekt zugreifen könne, falls mir bekannt ist, um welche Klasse es sich handelt? Ich müsste also anhand des Datas einmal auf das Intergefacte Objekt (Deutsch für Anfänger...) im Basisform und einmal direkt auf das Objekt (im abgeleiteten Form) zugreifen können...

[Edit]
Ähm... nochmals eine blöde Frage: Wie zerstöre ich mein Objekt im Data beim Neuaufbau der Listview?
Delphi-Quellcode:
for i:= 0 to ListViewMain.Items.Count - 1 do begin
  if Assigned( TAccountData( ListViewMain.Items[ i ].Data )) then begin
    //#1
    IDataObject( ListViewMain.Items[ i ].Data ).Free; //klappt ja nicht, da Interface
    //#2
    TAccountData( ListViewMain.Items[ i ].Data ).Free; //Geht nicht: Um diesen Cast handelt sich dieser Thread eigentlich :-)
    //#3
    obj:= IInterface( Pointer( ListViewMain.Selected.Data ));
    if Succeeded( obj.QueryInterface( IDataObject, obj3 )) then begin
      obj3.Free;// klappt ja auch nicht da Interface...
    end;
  end;
end;
Herzlichen Dank
RedOne

Geändert von RedOne (16. Jul 2010 um 10:27 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#17

AW: Data-Pointer in Interfacevariable casten

  Alt 16. Jul 2010, 10:24
Sehe ich es richtig dass es in Delphi 2007 keinen Weg gibt ein Interfaceobjekt, also mein Objekt im Data direkt in ein Objekt umzuwandeln, in TAccountData umzuwandeln?
jupp (fast)
http://www.delphipraxis.net/152892-v...ml#post1035377 (einige der Posts dieses Themas)
$2B or not $2B
  Mit Zitat antworten Zitat
RedOne

Registriert seit: 2. Jun 2008
71 Beiträge
 
Delphi XE2 Professional
 
#18

AW: Data-Pointer in Interfacevariable casten

  Alt 16. Jul 2010, 12:04
Ok. Wenigstens einen Lichtblick in der Zukunft

Eine kurze Frage noch zum löschen. Ich habe es jetzt so gemacht dass im Interface eine Funktion existiert die sich selbst als Objekt zurück gibt.

Delphi-Quellcode:
  IDataObject = interface( IInterface )
    ['{C94DCAC7-CA42-45D3-95D7-9BC321BA2E2A}']
    function Load( _id: Integer ): Boolean;
    function Insert: Boolean;
    function Update: Boolean;
    procedure Delete;
    procedure SetSelfObject( _o: TObject );
    function GetSelfObject: TObject;
  end;

procedure TAccountData.SetSelfObject( _o: TObject );
begin
  FSelfObject:= TAccountDataExtend( _o ); //Cast unnötig
end {SetSelfObject};

function TAccountData.GetSelfObject: TObject;
begin
  Result:= FSelfObject;
end {GetSelfObject};
Aber wie lösche ich mein Objekt nun wieder?
Mittels xy.GetSelfObject.Free wird eine "unnötige Zeigeroperation" ausgelöst.
Ich habe testweise bei der Generierung der Listview meine Objekte noch in eine TObjectList gespeichert und diese durchgeloopt. Auch dies gibt eine "unnötige Zeigeroperation". Blockiert mich das Interface? Wie wäre der korrekte/schöne Weg diese Objekte zu löschen?

Herzlichen Dank
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#19

AW: Data-Pointer in Interfacevariable casten

  Alt 16. Jul 2010, 12:11
Das Objekt hinter einem Interface löscht man nicht!

Wenn alle Referenzen auf das Objekt freigegeben sind (Variablen existieren nicht mehr oder wurden auf NIL, bzw. ein anderes Interface gesetzt), dann gibt sich das Interface selber frei,
also dann, wenn es keiner mehr braucht.

[edit]
ach menno, hier sinds ja 2 Objekte ... hmmm

"unnötige Zeigeroperation" ... meinst du vielleich "ungültige" ?

Wenn ja, dann ist der Zeiger falsch, bzw. zeigt nicht auf (d)ein Objekt.
$2B or not $2B

Geändert von himitsu (16. Jul 2010 um 12:14 Uhr)
  Mit Zitat antworten Zitat
RedOne

Registriert seit: 2. Jun 2008
71 Beiträge
 
Delphi XE2 Professional
 
#20

AW: Data-Pointer in Interfacevariable casten

  Alt 16. Jul 2010, 12:20
Ah cool. Eigentlich mag ich es nicht wenn mit jemand Arbeit abnimmt aber in diesem Fall...

Es ist doch nur ein Objekt. In "SelfObject" befindet sich nur eine weitere Referenz auf das Objekt.

Und ja, sollte natürlech "ungültige" heissen
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 2     12   


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:37 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz