Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Warum gibt es hier eine Acess Violation? (https://www.delphipraxis.net/198888-warum-gibt-es-hier-eine-acess-violation.html)

Benmik 7. Dez 2018 23:03

Warum gibt es hier eine Acess Violation?
 
In einem Modul deklariere ich:
Delphi-Quellcode:
type
  TDing = class(TObject)
    Nr: integer;
  end;
  TDingliste = class(TObjectList<TDing>)
  ...
  ...
  end;
In einem anderen Modul verwende ich das:
Delphi-Quellcode:
procedure Test;
var Dingliste:TDingliste;

  procedure Initialisiere;
  begin
    Dingliste := TDingliste.Create;
  end;

  procedure HierKnallts;
  var Ding:TDing;
  begin
    Ding := TDing.Create;
    Ding.Nr := 1;
    Dingliste.add(Ding); // Access Violation
  end;

begin
  Initialisiere;
  HierKnallts;
end;
Das verstehe ich nicht. Dingliste wird ordnungsgemäß erstellt und ist verfügbar. Nehme ich eine globale Variable für Dingliste, gibt es keine Probleme.

Schokohase 7. Dez 2018 23:17

AW: Warum gibt es hier eine Acess Violation?
 
Also mit 10.3 Rio kann ich nichts feststellen, da gibt es keine Exception.

Benmik 7. Dez 2018 23:38

AW: Warum gibt es hier eine Acess Violation?
 
Habe ich auch grad mal nachgebastelt, bei mir auch nicht. Dürfte ja auch gar nicht sein.
Woran könnte das theoretisch liegen?

Schokohase 7. Dez 2018 23:46

AW: Warum gibt es hier eine Acess Violation?
 
Beliebt ist
Delphi-Quellcode:
Ding.Create
statt
Delphi-Quellcode:
TDing.Create

Benmik 8. Dez 2018 00:04

AW: Warum gibt es hier eine Acess Violation?
 
Oouuh, jetzt fällt mir eine mögliche Antwort selber ein.

Die Routine "HierKnallts" ist eine Callback-Routine, deren aufrufende Routine von einer weiteren Unterprozedur von "Test" aufgerufen und mit @HierKnallts übergeben wird.
Obwohl man im Debugger die Variable ohne weiteres sieht, erinnere ich mich, dass es da mit der Sichtbarkeit Probleme gab. Daher funktioniert auch die globale Variable.

jaenicke 8. Dez 2018 01:29

AW: Warum gibt es hier eine Acess Violation?
 
Ich kann nur den Rat geben niemals Variablen oberhalb von Unterroutinen einer Methode zu deklarieren. Das führt nur zu Konfusionen. Man sollte die benötigten Variablen an Unterroutinen immer direkt mitgeben.

Sprich statt:
Delphi-Quellcode:
procedure Test;
var
  Dingliste:TDingliste;

  procedure HierKnallts;
  begin
    Dingliste.add(Ding); // Access Violation
  end;

begin
  ...
  HierKnallts;
end;
Besser:
Delphi-Quellcode:
procedure Test;

  procedure HierKnallts(ADingliste: TDingliste);
  begin
    ADingliste.add(Ding);
  end;

var
  Dingliste: TDingliste;
begin
  ...
  HierKnallts(Dingliste);
end;
So kann man viel leichter debuggen was da wo schief geht...

Zitat:

Zitat von Benmik (Beitrag 1420342)
Die Routine "HierKnallts" ist eine Callback-Routine, deren aufrufende Routine von einer weiteren Unterprozedur von "Test" aufgerufen und mit @HierKnallts übergeben wird.
Obwohl man im Debugger die Variable ohne weiteres sieht, erinnere ich mich, dass es da mit der Sichtbarkeit Probleme gab. Daher funktioniert auch die globale Variable.

Wenn es Probleme mit der Sichtbarkeit gäbe, würde es nicht kompilieren. Alles andere was mit Sichtbarkeit zu tun hat, würde nur zu dem Problem führen, wenn es irgendwo eine zweite gleichnamige Variable geben würde.

DP-Maintenance 8. Dez 2018 05:29

Dieses Thema wurde am "08. Dec 2018, 06:29 Uhr" von "Luckie" aus dem Forum "Algorithmen, Datenstrukturen und Klassendesign" in das Forum "Object-Pascal / Delphi-Language" verschoben.

hoika 8. Dez 2018 06:12

AW: Warum gibt es hier eine Acess Violation?
 
Hallo,
dein Code ist bestimmt nur dem richtigen Code nachgestellt. .?
Ichbdenke, Du hast die Listenvariable doppelt deklariert.

Uwe Raabe 8. Dez 2018 08:30

AW: Warum gibt es hier eine Acess Violation?
 
Zitat:

Zitat von jaenicke (Beitrag 1420343)
Man sollte die benötigten Variablen an Unterroutinen immer direkt mitgeben.

Das macht es auch wesentlich einfacher diese Unterroutinen in eigene Methoden zu extrahieren.

Benmik 8. Dez 2018 09:07

AW: Warum gibt es hier eine Acess Violation?
 
@jaenicke
Dein Ratschlag ist sicher analog zu dem, wenn irgend möglich keine globalen Variablen zu verwenden.
Delphi-Quellcode:
Dingliste
wäre dann praktisch eine globale Variable für die Unterroutinen von "Test". 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!)

@Uwe Raabe: Könntest du das kurz erläutern?

Ich wäre dankbar, wenn ich dieses Phänomen, das ich eigentlich kenne, mal verstehen würde.

Um etwas auszuholen: Gerade um meinen Code übersichtlich zu halten, teile ich ihn in möglichst viele einzelne Aufgaben auf. Eigentlich ist das gute Programmierpraxis; es gibt einen Guru, der gefordert hat, eine Routine dürfe immer nur eine einzige Aufgabe erledigen; ich habe ihn mal bei Wikipedia nachgeschlagen, leider aber seinen Namen vergessen und ich finde ihn auf die Schnelle auch nicht wieder.

Wenn ich mir professionellen Code anschaue, dann habe ich noch nie verstanden, warum diese Jungs sich oft einen Sch... um dieses Prinzip kümmern. Ellenlange Routinen, möglichst noch nach den Delphi Style Guides, was den Text noch doppelt so lang macht. Eine Qual, das zu debuggen. Ebenfalls eine Qual ist es, wenn die Aufgaben zwar in Unterroutinen zerlegt wurden, aber jede Routine einzeln bzw. eine eigene Methode ist. Man springt beim Debuggen dann zu zahllosen Stellen im Code hin und her.

Ich zerlege daher die Aufgaben einer Routine in Einzelteile. Und wenn diese Aufgaben nur einmal im ganzen Code vorkommen (oft 90%), dann verlege ich sie in eine Unterroutine und gebe ihr einen aussagekräftigen Namen, bei dem Aussagekraft vor (englischer) Kürze geht. So verstehe ich auch nach Monaten und Jahren noch, was da passiert. Kein Wunder, dass meine absolute Lieblingsneuerung damals Code Folding war! Wenn möglich, besteht meine Hauptroutine nur aus den Namen der 10 Unterprozeduren. Dank Ctrl+Shift-K-P liegt das ganze Gebilde kompakt vor mir. Daher würde ich jaenickes Ratschlag auch nur äußerst ungern folgen, denn so kann ich den Weg einer Variablen durch die Unterprozeduren leicht verfolgen. Mit F8 gehe ich durch die 10 Zeilen mit den Namen der Unterprozeduren und sehe viel leichter, wo etwas geschieht, was ich nicht beabsichtigt habe.

OK, nun zur Sache. Auch im besagten Beispiel handelt es sich um eine Klassenmethode mit mehreren Unterroutinen. Das Original von "Test" ist eine Unterprozedur dieser Klassenmethode und heißt
Delphi-Quellcode:
FindeDateien(VerzListe[i],'*',False,@FindeDateiRM,Abbrechen);
. Sie ruft
Delphi-Quellcode:
procedure FindeDateien(const BasisVerz,DateiMaske:string;MitUVerz:Boolean;FindeDateienRM:TFindeDateienRM;var Abbruch:Boolean);
auf, das in einer anderen Unit liegt, keine Klassenmethode ist und die Callback-Routine
Delphi-Quellcode:
procedure FindeDateiRM(const BasisVerz:string;const Data:TWin32FindDataW;var Abbrechen:Boolean);
aufruft, die ebenfalls eine Unterroutine der Klassenmethode ist. In
Delphi-Quellcode:
FindeDateiRM
wird eine Instanz von TBld erzeugt, die Dateieigenschaften von TWin32FindDataW eingefüllt und die Instanz dann zur Objektliste
Delphi-Quellcode:
TObjectList<TBld>
hinzugefügt.

Also so:

In einem anderen Modul:
Delphi-Quellcode:
type
TFindeDateienRM = procedure(const Verzname:string;const Data:_WIN32_FIND_DATAW;var Abbr:Boolean); stdcall;

procedure FindeDateien(const BasisVerz,DateiMaske:string;MitUVerz:Boolean;FindeDateienRM:TFindeDateienRM;var Abbruch:Boolean);
Delphi-Quellcode:
procedure TForm1.ErstelleListe;
var Bldliste:TBldliste;
begin

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

  procedure FindeDateiRM(const BasisVerz:string;const Data:TWin32FindDataW;var Abbrechen:Boolean);
  var Bld:TBld;
  begin
    Bld := TBld.Create;
    Bld.Name := string(Data.cFileName);
    Bldliste.add(Bld); // Das will er nicht
  end;

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

end;
Hier kommt es zu dem geschilderten Phänomen. Die
Delphi-Quellcode:
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?

Uwe Raabe 8. Dez 2018 09:44

AW: Warum gibt es hier eine Acess Violation?
 
Zitat:

Zitat von Benmik (Beitrag 1420351)
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.

Zitat:

Zitat von Benmik (Beitrag 1420351)
@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).

Zitat:

Zitat von Benmik (Beitrag 1420351)
Hier kommt es zu dem geschilderten Phänomen. Die
Delphi-Quellcode:
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
Delphi-Quellcode:
procedure
oder eine statische
Delphi-Quellcode:
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.

Benmik 8. Dez 2018 09:58

AW: Warum gibt es hier eine Acess Violation?
 
Ganz herzlichen Dank! Ich ahnte, dass da Dinge im Spiel sind, auf die ich selbst nicht kommen kann.

Zitat:

Zitat von Uwe Raabe (Beitrag 1420355)
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
Delphi-Quellcode:
TFindeDateienRM
ist allerdings nicht auf meinem Mist gewachsen, das habe ich irgendwoher. Das mit dem
Delphi-Quellcode:
stdcall
hätte ich ja nicht von allein gewusst.

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

Uwe Raabe 8. Dez 2018 10:13

AW: Warum gibt es hier eine Acess Violation?
 
Zitat:

Zitat von Benmik (Beitrag 1420356)
Wenn wir hier alles lassen würden, was sich für dich von selbst erklärt, dann können wir die DP eigentlich zumachen.

8-)


Zitat:

Zitat von Benmik (Beitrag 1420356)
Die Sache mit
Delphi-Quellcode:
TFindeDateienRM
ist allerdings nicht auf meinem Mist gewachsen, das habe ich irgendwoher. Das mit dem
Delphi-Quellcode:
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.

Benmik 8. Dez 2018 10:25

AW: Warum gibt es hier eine Acess Violation?
 
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?!

Uwe Raabe 8. Dez 2018 10:43

AW: Warum gibt es hier eine Acess Violation?
 
Zitat:

Zitat von Benmik (Beitrag 1420359)
FindeDateiRM steht bereits im interface-Teil einer Unit.

Dann gibt es also noch eine weitere FindeDateiRM neben der lokalen Unterroutine?

Zitat:

Zitat von Benmik (Beitrag 1420359)
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.

Zitat:

Zitat von Benmik (Beitrag 1420359)
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
Delphi-Quellcode:
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.

Benmik 8. Dez 2018 11:09

AW: Warum gibt es hier eine Acess Violation?
 
Das ist jetzt harter Stoff, aber das würde mich wirklich freuen, das mal zu kapieren.

Ich habe mich vertan:
Delphi-Quellcode:
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
Delphi-Quellcode:
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
Delphi-Quellcode:
TFindeDateienRM
)?

Uwe Raabe 8. Dez 2018 11:44

AW: Warum gibt es hier eine Acess Violation?
 
Die Deklaration von
Delphi-Quellcode:
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
Delphi-Quellcode:
TFindeDateiRM
nach deinen Bedürfnissen anpassen kannst, dann bietet sich folgende Lösung an:

Delphi-Quellcode:
TFindeDateienRM = procedure(const Verzname:string;const Data:_WIN32_FIND_DATAW;var Abbr:Boolean) of object;
Durch das
Delphi-Quellcode:
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
Delphi-Quellcode:
FindeDateien
wird dann jeweils
Delphi-Quellcode:
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;

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

Sollte die bisherige Deklaration von
Delphi-Quellcode:
TFindeDateiRM
allerdings extern vorgegeben sein, gibt es zwar auch Lösungen, die sind aber alle irgendwie hässlich.

Benmik 8. Dez 2018 12:18

AW: Warum gibt es hier eine Acess Violation?
 
Vielen Dank. Da mache ich mich mal dran.
Delphi-Quellcode:
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
Delphi-Quellcode:
FindeDateiRM
als Klassenmethode implementieren. Aber der Grund für meine Konstruktion mit der Unterprozedur war ja der, dass ich bei dieser Implementation innerhalb von
Delphi-Quellcode:
FindeDateiRM
auf Strukturen der Klassenmethode zugreifen will, die nicht global erreichbar sind.

Uwe Raabe 8. Dez 2018 13:27

AW: Warum gibt es hier eine Acess Violation?
 
Zitat:

Zitat von Benmik (Beitrag 1420369)
Aber der Grund für meine Konstruktion mit der Unterprozedur war ja der, dass ich bei dieser Implementation innerhalb von
Delphi-Quellcode:
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
Delphi-Quellcode:
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;

Benmik 8. Dez 2018 13:48

AW: Warum gibt es hier eine Acess Violation?
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1420371)
Von dieser Forderung wirst du dich verabschieden müssen.

Das ist doch mal ein klares Wort.
Zitat:

Zitat von Uwe Raabe (Beitrag 1420371)
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.

jaenicke 8. Dez 2018 20:38

AW: Warum gibt es hier eine Acess Violation?
 
Zitat:

Zitat von Benmik (Beitrag 1420351)
Daher würde ich jaenickes Ratschlag auch nur äußerst ungern folgen, denn so kann ich den Weg einer Variablen durch die Unterprozeduren leicht verfolgen. Mit F8 gehe ich durch die 10 Zeilen mit den Namen der Unterprozeduren und sehe viel leichter, wo etwas geschieht, was ich nicht beabsichtigt habe.

Das verlierst du doch gar nicht. Du hast ja die gleichen Variablen, die in der Hauptprozedur deklariert sind. Der Unterschied ist, dass du genau siehst welche davon an welche Unterroutine übergeben werden (das sollten natürlich nur die sein, die dort auch gebraucht werden).
Und innerhalb der Unterroutine siehst du beim Debuggen dann auch nur die dort relevanten lokalen Variablen.
Wenn man dann noch konsequent mit const und var arbeitet, kann der Compiler einerseits gut optimieren und andererseits sieht man wo eine Variable beschrieben werden kann.

Insofern ändert sich bezüglich des Debuggens nicht viel, aber die Komplexität nimmt ab (welche Variable wird eigentlich wo initialisiert?) und es ist übersichtlicher (weil man genau sieht was wo benutzt wird).

Benmik 8. Dez 2018 21:44

AW: Warum gibt es hier eine Acess Violation?
 
Da hast du recht. Ich hatte in letzter Zeit auch viel mit VBA zu tun, und da gilt die Überwachung einer Variable nur für eine Routine, oder sie muss global sein. Tatsächlich wertet der Debugger alles aus, was so heißt, wie man es mit F5 eingetragen hat. Wundert mich eigentlich, wo Pascal doch sonst so kleinlich ist.

Ich fürchte aber, ich bin nur schwer auf den Pfad der Tugend zu bringen. Zum einen sind das oft ziemlich viele prozedurweite Variablen, die kann ich ja nicht alle per Argument weitergeben. Ich weiß, das bedeutet schon mal, dass das Design nicht so dolle ist. Ich habe mich da auch schon gebessert. Aber mit diesen prozedurweiten Variablen habe ich eigentlich kaum je Probleme, also werde ich sie wohl heimlich weiterbenutzen und nicht darüber reden.8-)

jaenicke 8. Dez 2018 22:35

AW: Warum gibt es hier eine Acess Violation?
 
Ich arbeite auch mit altem Quelltext, in dem immer wieder mal relativ viele lokale Variablen in diversen Unterroutinen benutzt wurde. Deshalb weiß ich eben auch aus eigener bitterer Erfahrung wie problematisch das sein kann, insbesondere bei der Fehlersuche. ;-)

Uwe Raabe 8. Dez 2018 23:05

AW: Warum gibt es hier eine Acess Violation?
 
Zitat:

Zitat von Benmik (Beitrag 1420387)
Aber mit diesen prozedurweiten Variablen habe ich eigentlich kaum je Probleme

Das sieht man ja auch ganz deutlich an diesem Thread...

Benmik 10. Dez 2018 22:52

AW: Warum gibt es hier eine Acess Violation?
 
Hier noch ein Codesplitter, der in den Bereich des ursprünglichen Themas fällt.

Uwe Raabe 11. Dez 2018 06:23

AW: Warum gibt es hier eine Acess Violation?
 
Und da steht es ja auch schon genauso drin, daß es in dieser Situation crasht:

Zitat:

There is, however, a pitfall in the above example -
if the "inner" routine references any variable that was pushed onto
the stack before the "inner" procedure was called from testpass
(calltestpass parameters - if there were any, or local variables in
calltestpass - if there were any), your system most probably crashes:

Delphi-Quellcode:
{ This code compiles OK but generates runtime exception (could even be
  EMachineHangs :-) ) }
procedure testpass(p: pointer);
begin
  tProcedure(p);
end;
procedure calltestpass;
var msg: string;
 procedure inner;
 begin
   msg := 'hello';
   showmessage(msg);
 end;
begin
  testpass(@inner);
end;


Benmik 11. Dez 2018 14:33

AW: Warum gibt es hier eine Acess Violation?
 
Sí, Señor! Es steht aber auch drin, wie man das mit Assembler austrickst. Ein Vorgehen, das David Heffernan immer mit hochrotem Kopf als "filthy hack" geißelt.

Für mich lehrreich, weil ich ja im Leben nicht auf solche Hintergründe gekommen wäre. Es gibt halt mehr zwischen Himmel und Erde, als sich unserer Schulweisheit träumen lässt. Tröstlicherweise gilt das aber auch für die Experten.

Benmik 18. Dez 2018 17:04

AW: Warum gibt es hier eine Acess Violation?
 
Eine weitere Fundstelle ist AsyncCalls von Andreas Hausladen, und zwar die dortige Funktion LocalVclCall:
Delphi-Quellcode:
procedure TFormMain.MainProc;

  procedure DoSomething;

    procedure UpdateProgressBar(Percentage: Integer);
    begin
      ProgressBar.Position := Percentage;
      Sleep(20); // This delay does not affect the time for the 0..100 loop
                 // because UpdateProgressBar is non-blocking.
    end;

    procedure Finished;
    begin
      ShowMessage('Finished');
    end;

  var
    I: Integer;
  begin
    for I := 0 to 100 do
    begin
      // Do some time consuming stuff
      Sleep(30);
      LocalAsyncVclCall(@UpdateProgressBar, I); // non-blocking
    end;
    LocalVclCall(@Finished); // blocking
  end;

var
  a: IAsyncCall;
begin
  a := LocalAsyncCall(@DoSomething);
  a.ForceDifferentThread; // Do not execute in the main thread because this will
                          // change LocalAyncVclCall into a blocking LocalVclCall
  // do something
  //a.Sync; The Compiler will call this for us in the Interface._Release method
end;
Da frage ich mich, ob die auch darauf beruht, dass hier keine "innere" Variable zwischen den Aufrufen geändert wird.
Im Quelltext wird fleißig mit Inline-ASM gearbeitet, offenbar (auch) an den Stack-Adressen.

Warum ist diese Funktion übrigens als "Deprecated" gekennzeichnet? AsyncCalls wird seit 2016 nicht mehr weiterentwickelt, warum dann das "Deprecated" bei einzelnen Funktionen?


Alle Zeitangaben in WEZ +1. Es ist jetzt 18:18 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