AGB  ·  Datenschutz  ·  Impressum  







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

Warum gibt es hier eine Acess Violation?

Ein Thema von Benmik · begonnen am 7. Dez 2018 · letzter Beitrag vom 18. Dez 2018
Antwort Antwort
Seite 2 von 3     12 3      
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
10.995 Beiträge
 
Delphi 12 Athens
 
#11

AW: Warum gibt es hier eine Acess Violation?

  Alt 8. Dez 2018, 09:44
Es ist aber doch auch so, dass dies ein Design-Ratschlag ist und keinerlei technische Nachteile hat? (Delphi lässt ja diese Konstruktion explizit zu!)
Es gibt tatsächlich einen technischen Hintergrund. Die Variablen innerhalb einer Methode werden auf dem Stack angelegt. Der Zugriff erfolgt über einen Offset zum Stackregister. Wird nun innerhalb dieser Methode eine lokale Unterroutine aufgerufen ändert sich aber dieses Stackregister. Wenn nun diese Unterroutine auf Variablen der übergeordneten Methode zugreift, muss der Compiler erstmal den passenden Offset zum aktuellen Stackregister ermitteln. Das kostet Zeit und deshalb sind Zugriffe auf solche übergeordnete Variablen zeitaufwändiger als wenn diese als Var-Parameter an die Unterroutine übergeben werden.

@Uwe Raabe: Könntest du das kurz erläutern?
Na ja, erklärt isch doch eigentlich von selbst: Wenn man eine lokale Unterroutine in eine Klassenmethode umwandeln will, dann kann man eben nicht auf solche übergeordneten Variablen zugreifen. Bleibt also nur die Übergabe als Parameter oder Umwandlung der lokalen Variable in ein Feld der Klasse (wenn man globale Variablen mal ausschließt).

Hier kommt es zu dem geschilderten Phänomen. Die TObjectList ist bei der Klassenmethode deklariert und im Debugger in allen Unterprozeduren sichtbar. Trotzdem kommt es zu einer Schutzverletzung, wenn ich keine globale Variable verwende. Was genau geschieht da?
Der Fehler liegt in der Übergabe einer lokalen Methode (FindeDateiRM) als Prozedurtyp (TFindeDateienRM). Ich kann gar nicht glauben, daß der Compiler das durchgehen lässt. Vermutlich weil hier der @-Operator zum Einsatz kommt und damit die Typsicherheit umgangen wird. Die Deklaration von TFindeDateienRM lässt eigentlich nur eine globale procedure oder eine statische class procedure zu. Andernfalls passt der Stack nicht richtig. Je nachdem, was in der Methode gemacht wird kracht es eben oder kracht es nicht.

Berücksichtigt man nun das oben beschriebene Verhalten beim Zugriff auf die übergeordnete Variable, kommt es zum Crash, weil der Stack beim Aufruf des Callbacks eben nicht so ist, wie es die Unterroutine erwartet.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Benmik

Registriert seit: 11. Apr 2009
542 Beiträge
 
Delphi 11 Alexandria
 
#12

AW: Warum gibt es hier eine Acess Violation?

  Alt 8. Dez 2018, 09:58
Ganz herzlichen Dank! Ich ahnte, dass da Dinge im Spiel sind, auf die ich selbst nicht kommen kann.

Na ja, erklärt sich doch eigentlich von selbst...
Der ist gut. Wenn wir hier alles lassen würden, was sich für dich von selbst erklärt, dann können wir die DP eigentlich zumachen.

Ich muss das jetzt erstmal geistig durchdringen. Die Sache mit TFindeDateienRM ist allerdings nicht auf meinem Mist gewachsen, das habe ich irgendwoher. Das mit dem stdcall hätte ich ja nicht von allein gewusst.

Edit: Vermutlich findest du die ganze Konstruktion nicht gut, aber gibt es einen Weg, TFindeDateienRM so zu deklarieren, dass es geht?

Geändert von Benmik ( 8. Dez 2018 um 10:16 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
10.995 Beiträge
 
Delphi 12 Athens
 
#13

AW: Warum gibt es hier eine Acess Violation?

  Alt 8. Dez 2018, 10:13
Wenn wir hier alles lassen würden, was sich für dich von selbst erklärt, dann können wir die DP eigentlich zumachen.



Die Sache mit TFindeDateienRM ist allerdings nicht auf meinem Mist gewachsen, das habe ich irgendwoher. Das mit dem stdcall hätte ich ja nicht von allein gewusst.
Die Verwendung des @ ist schon ein Zeichen, daß der Compiler vorher irgendwo gemeckert hat. Deklariere mal die FindeDateiRM als globale Prozedur in der Unit, dann solltest du das @-Zeichen beim Aufruf von FindeDateien auch nicht brauchen. Die Tatsache, daß es manchmal funktioniert, heißt ja nicht, daß es richtig ist. Der Ersteller des originalen Codes hat vermutlich nur Dinge gemacht, die unkritisch waren.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Benmik

Registriert seit: 11. Apr 2009
542 Beiträge
 
Delphi 11 Alexandria
 
#14

AW: Warum gibt es hier eine Acess Violation?

  Alt 8. Dez 2018, 10:25
FindeDateiRM steht bereits im interface-Teil einer Unit.
Lasse ich das @ weg, kommt (wenig überraschend): Lokale Prozedur wurde Prozedurenvariable zugewiesen.

Ich verstehe auch nicht, warum das @ fehlen darf. Es wird doch die Adresse einer Prozedurenvariable übergeben?!
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
10.995 Beiträge
 
Delphi 12 Athens
 
#15

AW: Warum gibt es hier eine Acess Violation?

  Alt 8. Dez 2018, 10:43
FindeDateiRM steht bereits im interface-Teil einer Unit.
Dann gibt es also noch eine weitere FindeDateiRM neben der lokalen Unterroutine?

Lasse ich das @ weg, kommt (wenig überraschend): Lokale Prozedur wurde Prozedurenvariable zugewiesen.
Was ein untrügliches Zeichen ist, daß die Lokale Prozedur ein anderer Typ ist als die Prozedurenvariable. Deswegen kann man die auch nicht übergeben.

Ich verstehe auch nicht, warum das @ fehlen darf. Es wird doch die Adresse einer Prozedurenvariable übergeben?!
Das ist nicht ganz richtig: Man übergibt eine Prozedurenvariable, nicht deren Adresse. Eine Prozedurenvariable ist zwar intern auch im Wesentlichen die Adresse zu einer Prozedur, aber diese Prozedur muss gewissen Regeln gehorchen.

TFindeDateiRM ist als procedure(const Verzname:string;const Data:_WIN32_FIND_DATAW;var Abbr:Boolean); stdcall; deklariert. Eine lokale Prozedur hat aber trotz gleicher Signatur eine andere Aufrufkonvention. Deswegen sind die beiden nicht Zuweisungkompatibel. Mit dem @-Operator degradierst du die Lokale Prozedur aber zu einem typlosen Pointer und den kann man fast immer zuweisen. Das ändert aber nichts an der Inkompatibilität, sondern übergeht nur die (in diesem Fall durchaus sinnvolle) Typprüfung des Compilers.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Benmik

Registriert seit: 11. Apr 2009
542 Beiträge
 
Delphi 11 Alexandria
 
#16

AW: Warum gibt es hier eine Acess Violation?

  Alt 8. Dez 2018, 11:09
Das ist jetzt harter Stoff, aber das würde mich wirklich freuen, das mal zu kapieren.

Ich habe mich vertan: TFindeDateienRM = procedure(const Verzname:string;const Data:_WIN32_FIND_DATAW;var Abbr:Boolean); stdcall; steht im Interface-Teil. Ich dachte, ich brauche das, damit ich das als Variable übergeben kann. Die Prozedur procedure FindeDateiRM(const BasisVerz:string;const Data:TWin32FindDataW;var Abbrechen:Boolean); stdcall; habe ich mehrfach in meinem Code im Einsatz, weil er jeweils verschiedene Aufgaben erledigt und dabei auf die verschiedensten Strukturen zugreifen muss. Realisiert ist das immer als Unterprozedur einer Klassenmethode, auf deren Variablen die Prozedur dann zugreift. Daher keine globale Prozedur. Wie kann man das sonst realisieren (ich meine TFindeDateienRM )?

Geändert von Benmik ( 8. Dez 2018 um 11:25 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
10.995 Beiträge
 
Delphi 12 Athens
 
#17

AW: Warum gibt es hier eine Acess Violation?

  Alt 8. Dez 2018, 11:44
Die Deklaration von TFindeDateiRM brauchst du schon um den Parameter zu deklarieren. Allerdings sind hier für die Übergabe keine lokalen Unterroutinen erlaubt, sondern nur globale Prozeduren oder statische Klassenmethoden. Meiner Meinung nach fehlt bei dieser Deklaration noch ein Kontext.

Wenn du die Deklaration von TFindeDateiRM nach deinen Bedürfnissen anpassen kannst, dann bietet sich folgende Lösung an:

TFindeDateienRM = procedure(const Verzname:string;const Data:_WIN32_FIND_DATAW;var Abbr:Boolean) of object; Durch das of object wird zu der Methoden-Adresse noch die Objekt-Instanz übergeben (die Prozedurvariable wird dadurch zu einer Methodenvariable). So kannst du unterschiedliche Klassen deklarieren, die auf die jeweiligen Bedürfnisse zugeschnitten sind. Als Parameter für FindeDateien wird dann jeweils MeineInstanz.FindeDateiRM angegeben.

Delphi-Quellcode:
TEineKlasse = class
public
  procedure FindeDateiRM(const Verzname:string;const Data:_WIN32_FIND_DATAW;var Abbr:Boolean);
end;

TAndereKlasse = class
public
  procedure FindeDateiRM(const Verzname:string;const Data:_WIN32_FIND_DATAW;var Abbr:Boolean);
end;

...
  EineInstanz := TEineKlasse.Create;
  try
    FindeDateien(VerzListe[i], '*', False, EineInstanz.FindeDateiRM, Abbrechen);
  finally
    EineInstanz.Free;
  end;
...
  AndereInstanz := TAndereKlasse.Create;
  try
    FindeDateien(VerzListe[i], '*', False, AndereInstanz.FindeDateiRM, Abbrechen);
  finally
    Anderenstanz.Free;
  end;

TEineKlasse und TAndereKlasse halten dann die nötigen Strukturen vor (z.B. BldListe ), mit denen die jeweilige Methode FindeDateiRM arbeiten muss.

Sollte die bisherige Deklaration von TFindeDateiRM allerdings extern vorgegeben sein, gibt es zwar auch Lösungen, die sind aber alle irgendwie hässlich.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Benmik

Registriert seit: 11. Apr 2009
542 Beiträge
 
Delphi 11 Alexandria
 
#18

AW: Warum gibt es hier eine Acess Violation?

  Alt 8. Dez 2018, 12:18
Vielen Dank. Da mache ich mich mal dran. TFindeDateiRM kann ich ohne Weiteres anpassen.

Es ist so, dass ich - hier - eigentlich allem folgen kann, was du sagst, aber es nicht zu meinem aktiven Wissen gehört, so dass ich darüber nicht einfach verfügen und die Zusammenhänge erkennen kann.

Falls du an diesem trüben Samstag die Zeit aufbringen kannst, würdest du mal die "hässlichen" Lösungen beschreiben?
Für den Lerneffekt und das aktive Wissen.

Edit: Ich habe es gerade mal probiert und bin schon auf die erste Schwierigkeit gestoßen. Ich muss dann ja FindeDateiRM als Klassenmethode implementieren. Aber der Grund für meine Konstruktion mit der Unterprozedur war ja der, dass ich bei dieser Implementation innerhalb von FindeDateiRM auf Strukturen der Klassenmethode zugreifen will, die nicht global erreichbar sind.

Geändert von Benmik ( 8. Dez 2018 um 12:33 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
10.995 Beiträge
 
Delphi 12 Athens
 
#19

AW: Warum gibt es hier eine Acess Violation?

  Alt 8. Dez 2018, 13:27
Aber der Grund für meine Konstruktion mit der Unterprozedur war ja der, dass ich bei dieser Implementation innerhalb von FindeDateiRM auf Strukturen der Klassenmethode zugreifen will, die nicht global erreichbar sind.
Von dieser Forderung wirst du dich verabschieden müssen. Es gibt keinen praktikablen Weg, wie du über ein Callback an eine lokale Variable einer Methode kommen kannst.

Was aber geht wäre sowas:

Delphi-Quellcode:
type
  TBldListeWrapper = class
  private
    FBldListe: TBldListe;
  public
    constructor Create(ABldListe: TBldListe);
    procedure FindeDateiRM(const BasisVerz:string;const Data:TWin32FindDataW;var Abbrechen:Boolean);
  end;

constructor TBldListeQrapper.Create(ABldListe: TBldListe);
begin
  inherited Create;
  FBldListe := ABldListe;
end;

procedure TBldListeWrapper.FindeDateiRM(const BasisVerz:string;const Data:TWin32FindDataW;var Abbrechen:Boolean);
var Bld:TBld;
begin
  Bld := TBld.Create;
  Bld.Name := string(Data.cFileName);
  FBldliste.add(Bld);
end;

procedure TForm1.ErstelleListe;
var Bldliste:TBldliste;
begin

  procedure Intialisiere;
  begin
    Bldliste := TBldliste.Create(True);
  end;

  procedure ListeDateienAuf;
  var
    wrapper: TBldListeWrapper;
  begin
    wrapper := TBldListeWrapper.Create(BldListe);
    try
      FindeDateien(VerzListe[i],'*',False,wrapper.FindeDateiRM,Abbrechen);
    finally
      wrapper.Free;
    end;
  end;

end;
Oder man erweitert TBldListe :
Delphi-Quellcode:
type
  TExtendedBldListe = class(TBldListe);
  public
    procedure FindeDateiRM(const BasisVerz:string;const Data:TWin32FindDataW;var Abbrechen:Boolean);
  end;

procedure TExtendedBldListe.FindeDateiRM(const BasisVerz:string;const Data:TWin32FindDataW;var Abbrechen:Boolean);
var Bld:TBld;
begin
  Bld := TBld.Create;
  Bld.Name := string(Data.cFileName);
  add(Bld);
end;


procedure TForm1.ErstelleListe;
var Bldliste:TExtendedBldliste;
begin

  procedure Intialisiere;
  begin
    Bldliste := TExtendedBldliste.Create(True);
  end;

  procedure ListeDateienAuf;
  begin
    FindeDateien(VerzListe[i],'*',False,BldListe.FindeDateiRM,Abbrechen);
  end;

end;
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Benmik

Registriert seit: 11. Apr 2009
542 Beiträge
 
Delphi 11 Alexandria
 
#20

AW: Warum gibt es hier eine Acess Violation?

  Alt 8. Dez 2018, 13:48
Von dieser Forderung wirst du dich verabschieden müssen.
Das ist doch mal ein klares Wort.
Was aber geht wäre sowas:
Das führe ich mir jetzt mal gedankendurchdringend zu Gemüte. Vielen Dank für deine Mühe und die Zeit, die du mir gewidmet hast.
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 3     12 3      

 

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:10 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