![]() |
Delphi-Version: 5
interface und variable des implementierenden objekts
Hallo,
wie der titel suggeriert geht es um ien verhalten welches mir jetzt schon öfter auf die füße gefallen ist. Ich schildere das problem mal an meinem aktuelen beispiel um evtl fehler meinerseits aufzuklären. Ich benutze recht exzessiv interfaces, da mein hauptprojekt die arbeit mit DLL's erfordert. Nehmen wir ein interface.
Delphi-Quellcode:
Es geht nun insbesondere um die Methoden
IDataChangeLink = interface
['{05247DCC-84D0-4A7D-AA35-27FABEE3BBE4}'] procedure InstallLink(const AModule: IDataModule); stdcall; procedure RemoveLink(const AModule: IDataModule); stdcall; procedure DataChanging(const AModule: IDataModule; AKey: PChar; ADataType: word); stdcall; procedure DataChanged(const AModule: IDataModule; AKey: PChar; ADataType: word); stdcall; procedure DataAdded(const AModule: IDataModule; AKey: PChar; ADataType: word); stdcall; procedure DataRemoved(const AModule: IDataModule; AKey: PChar; ADataType: word); stdcall; function GetFLags: LongWord; stdcall; procedure SetFlags(const Value: LongWord); stdcall; end;
Delphi-Quellcode:
und
GetFlags
Delphi-Quellcode:
. Ein objet, welches das ganze implementiert sieht nun so aus:
SetFlags
Delphi-Quellcode:
Ein Objekt dass dieses Interface verwendet sei mal so definiert;
TCustomDataModuleLink = class(TInterfacedObject, IDataChangeLink)
private FFlags : LongWord; protected function GetFLags: LongWord; stdcall; procedure SetFlags(const Value: LongWord); stdcall; ... public ... property Flags: LongWord read GetFlags write SetFlags; end; function TCustomDataModuleLink.GetFLags: LongWord; begin result := FFlags; end; procedure TCustomDataModuleLink.SetFlags(const Value: LongWord); begin FFlags := Value; end;
Delphi-Quellcode:
Jetzt erzeuge ich dieses Objekt in DLL A und übergebe eine Referenz (temp) auf
TCustomDataModule = class(TCountedInterfacedObject, IDataModule)
private FLink : IDataChangeLink; function GetLinked: boolean; stdcall; public constructor Create(AName: PChar; ADataLink: IDataChangeLink = nil); reintroduce; virtual; destructor Destroy; override; //IDataModuleInterface procedure GetLink(out Link: IInterface); stdcall; procedure SetLink(const ALink: IInterface); stdcall;
Delphi-Quellcode:
an ein Objekt in DLL B. in DLL B rufe ich dann
IDataModule
Delphi-Quellcode:
Überprüfe ich nach diesem Aufruf den Wert Flags von lLink, dann ist der auch richtig geändert worden. Jetzt wird es für mich unverständlich. In einer weiteren Methode greife ich auf den Wert Flags zu;
procedure TDataModuleInspector.ChangeFlags(const temp: IDataMOdule; AFlag: LongWord);
var lLink: IDataCHangeLink; begin temp.GetLink(IInterface(LLink)); if Assigned(lLink) then try lLink.SetFlags(lLink.GetFlags xor AFlag); finally lLink := nil; end; end;
Delphi-Quellcode:
Wobei temp an diese Methode übergeben wurde. hier hat sich jedoch nichts getan!!!! egal wie oft ich
function TDataModuleInspector.AllowWrite(const ADataModule: IDataModule): boolean;
var lLink: IDataChangeLink; begin result := false; if not Assigned(ADataModule) then exit; lLink := nil; if ADataModule.Linked then ADataModule.GetLink(IInterface(lLink)); if Assigned(lLink) then try result := (lLink.GetFlags and DML_ALLOW_WRITE) = DML_ALLOW_WRITE; finally lInspLink := nil; lLink := nil; end; end;
Delphi-Quellcode:
oben aufrufe, der Wert den ich erhalten wenn ich danach GetFlags aufrufe ist immer noch der gleiche!!!! Was mache ich falsch?
ChangeFlags
Um das ganze mal zusammenzufassen. Ich habe es jetzt schon oft bemerkt, dass eine referenz auf ein interface, welche in einer anderen DLL verwendet wird schwierigkeiten hat, Variablen im implementierenden objekt zu setzen. Es scheint, als ob während der methode, in der eine variable gesetzt wird, so etwas wie temporärrer speicher angefordert wird, in dem dann eine kopie des objektes abgelegt wird. nach dem aufruf wird das ganze dann wieder freigegeben. So würde ich dannauch während des Aufrufes "sehen" wie die Variable gesetzt wird, danach die Änderung aber wieder verworfen wird. Das lustige ist ja, dass wenn ich in dem Modul, in dem das objekt erstellt wurde die Interface-Methoden verwende um lokale Variablen zu setzen, diese dann auch bestehen bleiben. Ich wundere mich allerdings auch darüber, dass ich dieses Verhalten nicht durchweg erhalte. Manchmal funktionieren dies Aufrufe und ändern tatsächlich die lokalen Variablen. |
AW: interface und variable des implementierenden objekts
Hallo Sebastian, müssen es unbedingt interfaces sein? Falls nein, dann eventuell so:
Delphi-Quellcode:
type
TContainer = class(TObject) protected DataModule: IDataModule; .. Class1: TClass1; Class2: TClass2; .. procedure InstallLink(.. procedure RemoveLink(.. procedure DataChanging(.. .. constructor Create; destructor Destroy; override; end; TClass = class(TContainer) .. constructor Create; destructor Destroy; override; end; |
AW: interface und variable des implementierenden objekts
Ich denke hier wird der Segen des automatischen Freigebens von Interfaces einfach zum Fluch, woher soll der Compiler wissen dass da nochmals jemand drauf zugreift, wenn der Zugriff aus einer DLL erfolgt und im Hauptprogramm keiner mehr Interesse dafür zu haben scheint.
|
AW: interface und variable des implementierenden objekts
Leite dein Interface am besten von IUnknown ab, dann wird COM benutzt. Das sollte korrekt funktionieren.
Benutzt du die Referenzzählung auch zur Freigabe der Objekte? Wenn nein, ist dein Grundkonzept bedenklich, da du nie sicher sein kannst, dass das Objekt hinter dem Interface noch existiert, wenn du es benutzt. Denn wenn du parallel das Objekt nutzt und ggf. freigibst, bekommen gespeicherte Referenzen davon nichts mit... Zitat:
|
AW: interface und variable des implementierenden objekts
@Bummi War auch mein erster Gedanke...
|
AW: interface und variable des implementierenden objekts
Nee ich gebe die referenzen nur ordnungsgemäß via := nil frei, und das objekt dahinter wird gar nicht in einer referenz gespeichert. benutze tatsächlich blos die reinen interfaces. Was hat es mit IUnknown und COM denn auf sich? in der deklaration ist doch IUnknown = IInterface definiert (system.pas)
zu dem thema DLL's, ich hatte jetzt in den letzten tagen mal etwas zeit um das zu untersuchen, war aber leider nciht mehr hier im forum. habe den fehler etwas eindämmen können:
Delphi-Quellcode:
Das PlugIn kommt jetzt aus irgendeiner DLL, meldet sich beim Server an und der initialisiert dann das PlugIn über den ResourcenManager.
IEnumerator = interface(IINterface)
procedure Reset; stdcall; procedure GetFirst(out obj); stdcall; function GetLast(out obj): integer; stdcall; function MoveNext: boolean; stdcall; function GetCurrent(out obj): integer; stdcall; end; IDataModule = interface(IEnumerator) ... end; IStreamModule = interface(IDataModule) procedure LoadFromStream(const AStream: IStream; Size: LongWord); stdcall; ... end; TResourceManager = class(TCustomPlugIn) procedure InitPlugIn(const PlugIn: IPlugIn); end; TSomePlugIn = class(TCustomPlugIn) FData: IDataModule; procedure ReadProperties(const Data: IDataModule); end; TResourceManager.InitPlugIn(const PlugIn: IPlugIn); var temp : IStreamModule; tempData: IDataModule; Stream : IStream; StatStg : TStatStg; sSize : integer; chSize : Int64; begin temp := TStreamDataModule.Create; Stream = TStreamAdapter.Create(TFileStream.Create(PlugIn.ResourceFile, fmReadOnly), soOwned); try ... Stream.Seek(0, soFromBeginning, ChSize); Stream.Stat(stStat, sSize); temp.LoadFromStream(Stream, stStat.cbSize); PlugIn.ReadProperties(temp); // --> hier ignoriert er teilweise änderungen if temp.QueryInterface(IDataModule, tempData) = S_Ok then PlugIn.ReadProperties(tempData); // --> damit klappt es besser, gelegentlich aber auch nicht finally tempData := nil; Stream := nil; temp := nil; end; end; Damit das PlugIn seine Resourcen aus dem Datenmodul lesen kann, muss es durch die enthaltenen Daten durchmanövrieren, dazu hat das Datenmodul ein Enumerator-Interface. Im Reset wird dann ein Zähler des Datenmoduls auf -1 gesetzt und mit MoveNext erhöht. Nach dem Lesen der Daten aus dem Stream, hat der Zähler jedoch den Wert der Anzahl an Items im Module, also muss das PlugIn vor dem Lesebeginn diesen resetten, klar. Das klappt aber dann nicht mehr zuverlässig, kurioserweise hilft dann im extremfall, ein neustart |
AW: interface und variable des implementierenden objekts
Und Du bist Dir sicher, das das immer das gleiche Objekt ist?
Deklariere dir einfach mal eine Eigenschaft 'UniqueID' des exportierten Objektes, dem Du im Konstruktor eine eindeutige ID zuweist (GetTickCount). Dann prüf doch mal, ob die UniqueID immer die gleiche ist. |
AW: interface und variable des implementierenden objekts
Zitat:
Es gibt nur einen Zähler, und dem Compiler ist es egal ob und wo dessen Referenz hoch/runtergezählt wird. Der Compiler ruft einfach nur Stur _AddRef und _Release auf und es ist ihm vollkommen schnuppe ob und was für ein Intferace darin steckt oder wo das herkommt. |
AW: interface und variable des implementierenden objekts
Ich hätte in diesem Fall auch lieber mal den Code von GetLink gesehen. Auch der Cast auf Interface bei dem out-Parameter sieht nicht ganz koscher aus.
|
AW: interface und variable des implementierenden objekts
der code von Get/Set -Link:
Delphi-Quellcode:
Edit: FLink ist vom Type IDataChangeLink
procedure TCustomDataModule.SetLink(const ALink: IInterface);
begin if Assigned(ALink) then begin if not supports(ALink, IDataChangeLink) then raise Exception.Create('SetLink, Invalid Interface-Type'); if Assigned(FLink) then RemoveLink; ALink.QueryInterface(IDataChangeLink, FLink); FLink.InstallLink(self); end; end; procedure TCustomDataModule.GetLink(out Link); begin // IDataChangeLink(Link) := FLink; FLink.QueryInterface(IDataChangeLink, Link); end; |
AW: interface und variable des implementierenden objekts
Ein untypisierter Parameter für die Rückgabe eines Interface scheint mir sehr verdächtig.
Wird hier eventuell die Referenzzählung ausgetrickst? Warum nicht so:
Delphi-Quellcode:
procedure TCustomDataModule.GetLink(out Link: IDataChangeLink);
begin Link := FLink; end; |
AW: interface und variable des implementierenden objekts
Zitat:
Und das Ergebnis des Ganzen? Speicherlecks. Denn ist vorher schon was in dem Link-Parameter drin, dann wird es ignoriert und nicht freigegeben. Für eine automatische Referenzzählung ist das OUT hier also der nahezu sichere Tot. |
AW: interface und variable des implementierenden objekts
Zitat:
// EDIT: Nicht einmal bei Delphi 2007 passiert das bei mir. // EDIT2: Und auch bei Delphi 3 nicht. Das heißt so allgemein lässt sich das auf jeden Fall nicht sagen. Ich bekomme das nicht hin, dass das Problem auftritt. |
AW: interface und variable des implementierenden objekts
OUT-Parameter sind Ausgabeparameter und da werden Eingangswerte nicht beachtet.
Dieses Problem sollte also immernoch bestehen. Wenn nicht und Emba hat dieses Verhalten geändert, dann ist das ein Bug und Emba hat das gefälligst wieder auszubauen. :roll: "Böse" ist eigentlich, daß der Eingangswert des Ausgangsparameters beachtet wird, was eigentlich nicht gemacht werden dürfte. :gruebel: const/in = in ... ingibt es in Delphi nicht, aber es entspricht quasi dem Parameter ohne eine Angabe von var/out/const var = in&out out = out Gut, Delphi scheint das Interface die Variable, vor Übergabe an den OUT-Parameter, freizugeben. Letzteres SetOutI müßte in etwa das tun, was diese WMI-Schnittstelle macht ... also den Eingangsparameter zu ignorieren. Was sagt denn ein älteres Delphi zu dem Code?
Delphi-Quellcode:
type
TTestIntf = class(TInterfacedObject, IInterface) FStrings: TStrings; constructor Create(SL: TStrings); destructor Destroy; override; end; constructor TTestIntf.Create(SL: TStrings); begin inherited Create; FStrings := SL; FStrings.Add('TTestIntf.Create ' + IntToHex(Integer(Self), 8)); end; destructor TTestIntf.Destroy; begin FStrings.Add('TTestIntf.Destroy ' + IntToHex(Integer(Self), 8)); inherited; end; procedure TForm6.SetVar(var Intf: IInterface); begin Memo1.Lines.Add('SetVar begin'); Intf := TTestIntf.Create(Memo1.Lines); Memo1.Lines.Add('SetVar end'); end; procedure TForm6.SetOut(out Intf: IInterface); begin Memo1.Lines.Add('SetOut begin'); IInterface(Intf) := TTestIntf.Create(Memo1.Lines); Memo1.Lines.Add('SetOut end'); end; procedure TForm6.SetVarD(var Intf); begin Memo1.Lines.Add('SetVarD begin'); IInterface(Intf) := TTestIntf.Create(Memo1.Lines); Memo1.Lines.Add('SetVarD end'); end; procedure TForm6.SetOutD(out Intf); begin Memo1.Lines.Add('SetOutD begin'); IInterface(Intf) := TTestIntf.Create(Memo1.Lines); Memo1.Lines.Add('SetOutD end'); end; procedure TForm6.SetOutI(out Intf); var I: IInterface; begin Memo1.Lines.Add('SetOutI begin'); I := TTestIntf.Create(Memo1.Lines); I._AddRef; Pointer(Intf) := Pointer(I); Memo1.Lines.Add('SetOutI end'); end; var Intf: IInterface; Memo1.Lines.Add(''); Memo1.Lines.Add('create test-interface'); Intf := TTestIntf.Create(Memo1.Lines); Memo1.Lines.Add('call SetVar'); SetVar(Intf); Memo1.Lines.Add('set nil'); Intf := nil; Memo1.Lines.Add(''); Memo1.Lines.Add('create test-interface'); Intf := TTestIntf.Create(Memo1.Lines); Memo1.Lines.Add('call SetOut'); SetOut(Intf); Memo1.Lines.Add('set nil'); Intf := nil; Memo1.Lines.Add(''); Memo1.Lines.Add('create test-interface'); Intf := TTestIntf.Create(Memo1.Lines); Memo1.Lines.Add('call SetVarD'); SetVarD(Intf); Memo1.Lines.Add('set nil'); Intf := nil; Memo1.Lines.Add(''); Memo1.Lines.Add('create test-interface'); Intf := TTestIntf.Create(Memo1.Lines); Memo1.Lines.Add('call SetOutD'); SetOutD(Intf); Memo1.Lines.Add('set nil'); Intf := nil; Memo1.Lines.Add(''); Memo1.Lines.Add('create test-interface'); Intf := TTestIntf.Create(Memo1.Lines); Memo1.Lines.Add('call SetOutI'); SetOutI(Intf); Memo1.Lines.Add('set nil'); Intf := nil; |
AW: interface und variable des implementierenden objekts
Zitat:
Zitat:
Code:
Ich würde mal sagen alles ok, oder?
create test-interface
TTestIntf.Create 008A3738 call SetVar SetVar begin TTestIntf.Create 008A3750 TTestIntf.Destroy 008A3738 SetVar end set nil TTestIntf.Destroy 008A3750 create test-interface TTestIntf.Create 008A3738 call SetOut TTestIntf.Destroy 008A3738 SetOut begin TTestIntf.Create 008A3738 SetOut end set nil TTestIntf.Destroy 008A3738 create test-interface TTestIntf.Create 008A3738 call SetVarD SetVarD begin TTestIntf.Create 008A3750 TTestIntf.Destroy 008A3738 SetVarD end set nil TTestIntf.Destroy 008A3750 create test-interface TTestIntf.Create 008A3738 call SetOutD TTestIntf.Destroy 008A3738 SetOutD begin TTestIntf.Create 008A3738 SetOutD end set nil TTestIntf.Destroy 008A3738 create test-interface TTestIntf.Create 008A3738 call SetOutI TTestIntf.Destroy 008A3738 SetOutI begin TTestIntf.Create 008A3738 SetOutI end set nil TTestIntf.Destroy 008A3738 |
AW: interface und variable des implementierenden objekts
Zitat:
Ich kann da ebensowenig ein Fehlverhalten des Compilers erkennen. |
AW: interface und variable des implementierenden objekts
in etwa so hatte ich das auch verstanden & beim debuggen auch gesehen. ich dachte halt, dies ist der sicherste weg die referenzzählung am leben zu halten, selbst wenn man ma ne interfacevariable übergibt die noch belegt ist.
aber hat jetzt jemand von euhc auch mal dieses mysteriöse verhalten das ich anfangs beschrieben hatte bemerkt? oder etwas ähnliches, dass sich im schlimmsten fall nur durch einen neustart des gesamten computers beheben lässt? ich habe damit wirklich ein problem dass ich so nicht lösen kann, insbesondere da dritte mit diesem programm arbeiten sollen und es im moment auch schon tun und es einfach nicht sein kann, dass in manchen momenten (soweit ich das beurteilen kann nicht vorhersehbar) wertezuweisungen einfach verworfen werden... falls nicht, kennt jemand möglichkeiten, wie man solchen problemen auf den grund gehen kann??? es kann ja durchaus meine eigene dummheit an irgendeiner anderen stelle sein... |
AW: interface und variable des implementierenden objekts
Erstell ein Minimalprojekt, bei dem der Fehler auftritt und entferne so lange Teile, bis der Fehler nicht mehr auftritt.
Wenn du nicht weiter kommst, stellst du die Quellen für das Minimalprojekt hier rein. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:51 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz