Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Zugriffsverletzung beim 2. Aufruf von Interface Methode (https://www.delphipraxis.net/155156-zugriffsverletzung-beim-2-aufruf-von-interface-methode.html)

WorstNightmare 11. Okt 2010 14:15

Delphi-Version: 2010

Zugriffsverletzung beim 2. Aufruf von Interface Methode
 
Hallo,

ich habe folgendes Interface:
Delphi-Quellcode:
  TFileInfo = packed record
    Name: WideString;
    Size: Integer;
  end;
  PFileInfo = ^TFileInfo;
  PFileInfos = array of PFileInfo;

  WideStrings = array of WideString;

  IDownloadPlugin = interface(ILoadPlugin)
    ['{30DABA9A-6B0B-44BB-8552-816DB0C68FD8}']
    procedure Init(Ifc: IDownload); stdcall;
    function GetFileInfo(Link: WideString): TFileInfo; stdcall;
    function GetFileInfoMulti(Links: WideStrings): PFileInfos; stdcall;
    procedure Download(Link: WideString); stdcall;
    procedure Premium(Link, Username, Password: WideString); stdcall;
    function GetAccountInfo(Username, Password: WideString): TAccountInfo; stdcall;
    function CheckFile(Contents: WideString): Boolean; stdcall;
  end;
Es wird mit Init initialisiert, dort wird auch ein anderes Interface übergeben, von dem das Plugin auch Methoden ausführt. IDownloadPlugin ist in einer DLL, IDownload im Host.
Nun rufe ich GetFileInfoMulti auf und bekomme beim ersten Mal auch die Zeiger auf die Datei-Informationen als Array wieder, verarbeite sie entsprechend und führe Dispose für alle Zeiger aus. "Links" und das zurückgegebene PFileInfos wird dann auf nil gesetzt.
Wenn ich die Methode dann ein zweites Mal mit einem anderen Array aufrufen will, gibt es beim Aufruf in der DLL eine A/V. Der erste Befehl der Methode wird nicht mehr erreicht, er hängt irgendwo dazwischen.

Dies ist der Code im Host, der 2. Aufruf (der fehlschlägt) findet statt wenn mehr als 40 Links verarbeitet werden müssen und es somit mehrere Durchläufe in der Schleife gibt.
Delphi-Quellcode:
var
  i, Loops, j: Integer;
  Infos, InfoSplit: PFileInfos;
  LinkList, Split: WideStrings;
  Hoster: string;
begin
 ....
    Loops := Ceil(Length(LinkList) / 40);

    for i := 0 to Loops - 1 do
    begin
      Split := Copy(LinkList, i * 40, 40);

      // Da eh alle das selbe Plugin haben, verwenden wir einfach mal das vom 1. Link in der Liste
      TData(FHosterLinks[Hoster][0]).Load; // hier wird das Interface erstellt
      try
        InfoSplit := TData(FHosterLinks[Hoster][0]).PlgInstance.GetFileInfoMulti(Split);
      finally
        TData(FHosterLinks[Hoster][0]).Unload(True);
        Split := nil;
      end;

      // InfoSplit an die richtige Stelle im Infos array kopieren
      for j := 0 to High(InfoSplit) do
      begin
        SetLength(Infos, Length(Infos) + 1);
        Infos[High(Infos)] := InfoSplit[j];
      end;

      InfoSplit := nil;  // Interessant: Wenn ich diese Zeile herausnehme geht alles einwandfrei
    end;
Ich habe herausgefunden, dass wenn ich die Zeile wo InfoSplit := nil gesetzt wird rausnehme, alles geht. Warum ist das so?

Blup 11. Okt 2010 15:30

AW: Zugriffsverletzung beim 2. Aufruf von Interface Methode
 
Was passiert den bei Load, PlgInstance und Unload?

Das Interfaceobject darf bei Unload noch nicht freigegeben werden. Innerhalb von Prozeduren/Methoden verwendete Interfaces werden möglicherweise erst beim Verlassen oder bei erneuter Zuweisung eines anderen Interfaceobject freigegeben.

Meine Vermutung, der Compiler erzeugt daraus im Prinzip das:
Delphi-Quellcode:
   DummyInterfaceVariable := nil;
   {Schleife begin}
      Load; // hier wird das Interface erstellt
      try
        if Assigned(DummyInterfaceVariable) then
          DummyInterfaceVariable._Release; // <- hier knallts beim 2.Durchlauf

        DummyInterfaceVariable := PlgInstance;
        if Assigned(DummyInterfaceVariable) then
          DummyInterfaceVariable._AddRef;

        InfoSplit := DummyInterfaceVariable.GetFileInfoMulti(Split);
      finally
        Unload(True); // <- hier wird das Object vermutlich unzulässig freigegeben
        Split := nil;
      end;
   {Schleife end}
   if Assigned(DummyInterfaceVariable) then
     DummyInterfaceVariable._Release; // <- hier könnte es schon bei nur einem Durchlauf

Uwe Raabe 11. Okt 2010 16:23

AW: Zugriffsverletzung beim 2. Aufruf von Interface Methode
 
Was ist TData?

WorstNightmare 11. Okt 2010 16:31

AW: Zugriffsverletzung beim 2. Aufruf von Interface Methode
 
In Load() wird nur das Interface geholt
FPlgInstance := FHosterPlg.CreateInstance(Link);

CreateInstance wird auf die DLL weitergeleitet, wo einfach Result := TPlugin.Create gemacht wird.

In Unload wird anders als man denken könnte das Interface nicht freigegeben sondern es passieren andere Dinge die hier nicht von Bedeutung sind.

PlgInstance implementiert IDownload und leitet alle Aufrufe (z.B. GetFileInfoMulti) letztendlich auf IDownloadPlugin weiter.

Zitat:

Was ist TData?
Internes Verwaltungsobjekt welches das Interface hält.

Blup 12. Okt 2010 08:49

AW: Zugriffsverletzung beim 2. Aufruf von Interface Methode
 
Um den Fehler einzugrenzen würde ich erst mal so ändern:
Delphi-Quellcode:
var
  plugin: IDownloadPlugin;
{...}
  plugin := TData(FHosterLinks[Hoster][0]).PlgInstance;
  InfoSplit := plugin.GetFileInfoMulti(Split);
  plugin := nil;
Beim "Kopieren" der Infos werden ja nur neue Zeiger auf die jeweiligen TFileInfo-Strukturen angelegt. Ist sichergestellt das diese Daten auch über die gesamte Gültigkeit von "Infos" existieren und nicht überschrieben werden?

WorstNightmare 12. Okt 2010 18:02

AW: Zugriffsverletzung beim 2. Aufruf von Interface Methode
 
Zitat:

Beim "Kopieren" der Infos werden ja nur neue Zeiger auf die jeweiligen TFileInfo-Strukturen angelegt. Ist sichergestellt das diese Daten auch über die gesamte Gültigkeit von "Infos" existieren und nicht überschrieben werden?
Ich denke es wird nichts ausgeführt was diese Daten löschen würde, es sei denn das geschieht wenn ich InfoSplit := nil setze nachdem alle Zeiger nach "Infos" kopiert wurden. Denn wenn ich nur die Zeile rausnehme geht ja witzigerweise alles. Ich weiß aber nicht warum das dann den 2. Durchgang beeinflusst und nicht erst knallt wenn ich dann auf Infos wieder zugreifen möchte und die FileInfos weiterverarbeite...

shmia 12. Okt 2010 18:31

AW: Zugriffsverletzung beim 2. Aufruf von Interface Methode
 
Delphi-Quellcode:
Array of Irgendwas
ist nicht wirklich ActiveX/COM kompatibel.
Die geladene DLL verwendet nicht zwingend die gleiche Instanz des Memory-Managers wie die Hauptanwendung.
Auch der Record TFileInfo ist nicht so schön bzw ziemlich problematisch.
Stattdessen sollte es ein Interface IFileInfo und eine Klasse die das Interface implementiert geben.
Wenn man nur mit Objekten über ein Interface arbeitet, dann braucht man auch kein _AddRef oder _Release.

WorstNightmare 12. Okt 2010 18:52

AW: Zugriffsverletzung beim 2. Aufruf von Interface Methode
 
Mhmh das ist natürlich unschön. Durch was ersetze ich denn am besten Arrays?

Mir würde nur sowas einfallen:
Delphi-Quellcode:
IFileInfo = interface(IInterface)
  function Name: WideString;
  function Size: Integer;
  function Next: IFileInfo;
end;
Und dasselbe nochmal um die WideString arrays zu ersetzen:
Delphi-Quellcode:
IString = interface(IInterface)
  function Get: WideString;
  function Next: IString;
end;
Würde es so gehen? :/

Zitat:

Wenn man nur mit Objekten über ein Interface arbeitet, dann braucht man auch kein _AddRef oder _Release.
Wie meinen?

luki pescoller 13. Okt 2010 15:09

AW: Zugriffsverletzung beim 2. Aufruf von Interface Methode
 
habe ein ähnliches Problem,

ich hole mir das Interface von einem externen Treiber (*.ax)

vcap.QueryInterface(Aguid, AFilter);

MyFilter := AFilter as TMyFilter;

MyFilter.SetShutterSpeed(v); ...

geht gut, ein weiterer Aufruf liefert einen Systemfehler

außerdem kriege ich unter 64bit keinen Pointer auf das Interface (AFilter), nur unter 32bit.

gruss
lukas

shmia 13. Okt 2010 15:36

AW: Zugriffsverletzung beim 2. Aufruf von Interface Methode
 
Ich würd's so machen:
Delphi-Quellcode:
IFileInfo = interface(IInterface)
  ['{C7... '] // GUID nicht vergessen
  function Name: WideString;
  function Size: Integer;
end;

IFileInfoList = interface(IInterface)
  ['{C7... '] // GUID nicht vergessen
  function Get_Count:integer;
  function Get_Item(idx:integer):IFileInfo;
  property Count:integer read Get_Count;
  property Item[idx:integer] read Get_Item;
end;
Als Ersatz für Array of Widestring würde ich das Interface IStrings (aus Unit StdVCL) verwenden.

Mit der Funktion GetOleStrings() kannst du aus einem TString-Objekt ein IStrings-Objekt erzeugen.


Alle Zeitangaben in WEZ +1. Es ist jetzt 15:07 Uhr.
Seite 1 von 2  1 2      

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