| Autor |
Beitrag |
freedy
      
Beiträge: 403
Erhaltene Danke: 1
Winows 7
Delphi XE
|
Verfasst: Do 27.05.10 09:34
Hallo Forum,
ich habe ein merkwürdiges Phänomen. In einer eigenen Komponente tritt eine Zugriffsverletzung bei deren Freigabe auf, die ich mir nicht erklären kann. Das Projekt wurde mit Delphi 2006 erstellt und funktioniert in allen anderen Projekten fehlerfrei bzw. lässt sich der Fehler dort nicht reproduzieren.
Grobe Symptomatik:
- In der Komponente gibt es mehrere Instanzen von TPicture als Property (published).
- Um PNGs darzustellen, nutze ich das PNGImage von Gustavo Huffenbacher Daud SourceForge (leider down)
- Bei der Freigabe werden die Instanzen aufgeräumt. Sobald jedoch ein PNG geladen ist und die Freigabe erfolgen soll, gibt es eine Zugriffsverletzung. In meinem Demoprojekt und mehreren anderen Versuchen klappt alles. Nur ein größeres Projekt haut mir alles um die Ohren. Bei anderen Datenformaten (BMP, ICO etc.) funktioniert es.
Sämtliche Zugriffe auf die Properties habe ich überprüft und zu Testzwecken sogar entfernt. Ich weiß nur, dass der Verweis auf die Instanzen von TPicture (Code siehe unten) korrekt sind. Wird dort jedoch bei der Freigabe auf die Eigenschaft Graphic zugegriffen, gibt es die Zugriffsverletzung, wenn ein PNG geladen war. Beim Zeichnen der Grafik auf ein Canvas passiert allerdings nichts. Auf das Bild wird ohne Probleme zugegriffen.
Ich habe inzwischen keine Ahnung mehr, wo ich noch suchen kann. Anbei habe ich mal das PNG-Package gehängt.
Hier noch die relevanten Ausschnitte aus meinem Code:
Klassendeklaration
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21:
| type TToolPalette = class(TCustomForm) private ... FPicture: TPicture; FHotPicture: TPicture; ... procedure SetPicture(const Value: TPicture); procedure SetHotPicture(const Value: TPicture); ... protected procedure PictureChange(Sender : TObject); ... public constructor CreateNew(AComponent : TComponent; Dummy : Integer = 0); override; destructor Destroy; override; published ... property Picture : TPicture read FPicture write SetPicture; property HotPicture : TPicture read FHotPicture write SetHotPicture; ... end; |
Konstruktor
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| constructor TToolPalette.CreateNew(AComponent: TComponent; Dummy: Integer); begin inherited;
FPicture := TPicture.Create; FPicture.OnChange := PictureChange;
FHotPicture := TPicture.Create; FHotPicture.OnChange := PictureChange;
FDoSize := true;
Color := $00FAF1E9; BorderStyle := bsNone; end; |
Die Freigabe:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20:
| destructor TToolPalette.Destroy; begin if (FPicture <> nil) then begin FPicture.OnChange := nil; FPicture.OnProgress := nil; FreeAndNil(FPicture); end;
if (FHotPicture <> nil) then begin FHotPicture.OnChange := nil; FHotPicture.OnProgress := nil; FreeAndNil(FHotPicture); end;
...
inherited; end; |
Anmerkung: Die Set-Methoden werden mit Assign ausgeführt.
Einloggen, um Attachments anzusehen!
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Do 27.05.10 11:53
Was sagt MadExcept/EurekaLog?
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
freedy 
      
Beiträge: 403
Erhaltene Danke: 1
Winows 7
Delphi XE
|
Verfasst: Do 27.05.10 12:41
Hallo Benny!
Stimmt, die Log-Datei hätte ich anhängen können. Wirklich schlau werde ich daraus allerdings nicht, da mir lediglich der Call Stack etwas sagt. Vom Rest habe ich nicht sooo viel Ahnung.
Kannst du da was draus lesen?
Grüße,
Michael
Einloggen, um Attachments anzusehen!
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Do 27.05.10 17:28
hmmm, sollte der Contructor nicht eigentlich nur Create heißen? Von daher tippe ich mal auf eine etwas unsaubre Initialisierung.
Aber zum Stacktrace:
Welche Zeile innerhalb des Constructors trifft die Exception denn eigentlich?
Die Assigned-Prüfung sollte nicht mit <>Nil, sondern Assigned gemacht werden (k, führt intern den gleichen Code aus, kann aber in zukünftigen Compiler-Versionen durchaus auch durch ne sauberere Prüfung ersetzt werden (weil eigentlich alles auf der ersten und letzten Memory Page nicht zugreifbar ist, nicht nur das Byte 0).
Dass man die Properties einer Klasse nicht abfangen sollte, wenn diese als Objekt nach außen sichtbar ist, ist ein anderer Punkt.
Basierend auf dem Dissambly und den Register-Inhalten liegt die Vermutung nahe, dass die TPicture-Instanzen bereits durch die VCL freigegeben werden. Dort bitte einfach mal schauen, ob Du an der Stelle etwas durchtracen kannst (Debug-DCUs einschalten und bei TPicture.Destroy nen Breakpoint setzen).
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
freedy 
      
Beiträge: 403
Erhaltene Danke: 1
Winows 7
Delphi XE
|
Verfasst: Do 27.05.10 17:40
BenBE hat folgendes geschrieben : | | hmmm, sollte der Contructor nicht eigentlich nur Create heißen? Von daher tippe ich mal auf eine etwas unsaubre Initialisierung. |
Nein, da die Klasse von TCustomForm erbt, muss ich meine Initialisierung in CreateNew vornehmen. So wird es auch für TForm gemacht. Das Create von TCustomForm ruft intern wiederum CreateNew auf, so dass alle Initialisierungen stattfinden können.
BenBE hat folgendes geschrieben : | Aber zum Stacktrace:
Welche Zeile innerhalb des Constructors trifft die Exception denn eigentlich?
|
Das Destroy von TPicture löst ein Free für die Graphic aus. Das ist im Call-Stack diese Stelle:
Quelltext 1:
| 51f265c4 +008 rtl100.bpl system 8849 +1 System.TObject.Free |
Da Graphic aber schon ins Nirvana zu zeigen scheint, gibt es den Ausnahmefehler. Wer Graphic allerdings freigibt und den Zeiger nicht löscht, weiß ich noch nicht.
BenBE hat folgendes geschrieben : | Die Assigned-Prüfung sollte nicht mit <>Nil, sondern Assigned gemacht werden (k, führt intern den gleichen Code aus, kann aber in zukünftigen Compiler-Versionen durchaus auch durch ne sauberere Prüfung ersetzt werden (weil eigentlich alles auf der ersten und letzten Memory Page nicht zugreifbar ist, nicht nur das Byte 0).
|
Gebe dir einerseits Recht, auf der anderen Seite versuche ich, Funktionsaufrufe zu vermeiden, wenn es dadurch nicht zu einer drastischen Verschlechterung der Lesbarkeit kommt. Und da im Assigend wirklich der gleiche Code steht, macht das an dieser Stelle erstmal nichts, denke ich.
BenBE hat folgendes geschrieben : | Dass man die Properties einer Klasse nicht abfangen sollte, wenn diese als Objekt nach außen sichtbar ist, ist ein anderer Punkt.
|
Und wie würdest du das lösen? So kann ich sicher sein, dass das Objekt immer existiert.
BenBE hat folgendes geschrieben : | | Basierend auf dem Dissambly und den Register-Inhalten liegt die Vermutung nahe, dass die TPicture-Instanzen bereits durch die VCL freigegeben werden. Dort bitte einfach mal schauen, ob Du an der Stelle etwas durchtracen kannst (Debug-DCUs einschalten und bei TPicture.Destroy nen Breakpoint setzen). |
Scheint mir auch so. Ich gebe die Instanz jetzt manuell im FormDestory frei. Damit funktioniert es wieder. Sehr merkwürdig. 
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Do 27.05.10 18:07
freedy hat folgendes geschrieben : | BenBE hat folgendes geschrieben : | | hmmm, sollte der Contructor nicht eigentlich nur Create heißen? Von daher tippe ich mal auf eine etwas unsaubre Initialisierung. |
Nein, da die Klasse von TCustomForm erbt, muss ich meine Initialisierung in CreateNew vornehmen. So wird es auch für TForm gemacht. Das Create von TCustomForm ruft intern wiederum CreateNew auf, so dass alle Initialisierungen stattfinden können. |
Verwundert mich etwas. Weil nach Konvention ist eigentlich nur Create als Constructor gekennzeichnet; alles andere wäre ne Proc. Schon allein deshalb, weil der Aufruf eines Constructors vs. einer Prozedur ein klein wenig anders abläuft.
freedy hat folgendes geschrieben : | BenBE hat folgendes geschrieben : | Aber zum Stacktrace:
Welche Zeile innerhalb des Constructors trifft die Exception denn eigentlich?
|
Das Destroy von TPicture löst ein Free für die Graphic aus. Das ist im Call-Stack diese Stelle:
Quelltext 1:
| 51f265c4 +008 rtl100.bpl system 8849 +1 System.TObject.Free |
Da Graphic aber schon ins Nirvana zu zeigen scheint, gibt es den Ausnahmefehler. Wer Graphic allerdings freigibt und den Zeiger nicht löscht, weiß ich noch nicht. |
Schau mal, ob Du rausbekommst, wie der Stacktrace aller TPicture.Free-Aufrufe sind.
Dass der Zeiger noch unsauber ist, hängt damit zusammen, dass Du nicht über die Freigabe des TPicture informiert wirst. Schau mal, ob Du da noch ein Event findest.
freedy hat folgendes geschrieben : | BenBE hat folgendes geschrieben : | Die Assigned-Prüfung sollte nicht mit <>Nil, sondern Assigned gemacht werden (k, führt intern den gleichen Code aus, kann aber in zukünftigen Compiler-Versionen durchaus auch durch ne sauberere Prüfung ersetzt werden (weil eigentlich alles auf der ersten und letzten Memory Page nicht zugreifbar ist, nicht nur das Byte 0).
|
Gebe dir einerseits Recht, auf der anderen Seite versuche ich, Funktionsaufrufe zu vermeiden, wenn es dadurch nicht zu einer drastischen Verschlechterung der Lesbarkeit kommt. Und da im Assigend wirklich der gleiche Code steht, macht das an dieser Stelle erstmal nichts, denke ich. |
Jep, war auch wirklich nur ne Anmerkung.
freedy hat folgendes geschrieben : | BenBE hat folgendes geschrieben : | Dass man die Properties einer Klasse nicht abfangen sollte, wenn diese als Objekt nach außen sichtbar ist, ist ein anderer Punkt.
|
Und wie würdest du das lösen? So kann ich sicher sein, dass das Objekt immer existiert. |
k, hatte mich etwas falsch ausgedrückt. Ging mir eigentlich eher um die Event-Handler der TPicture-Instanz, die Du überschreibst, gleichzeitig aber dem User volle Kontrolle über das TPitcutre-Objekt ässt. Macht TMemo mit der Lines-Eigenschaft zwar ähnlich, ist aber immer ne bescheidene Konstruktion aus Sicht der Abkapslung.
freedy hat folgendes geschrieben : | BenBE hat folgendes geschrieben : | | Basierend auf dem Dissambly und den Register-Inhalten liegt die Vermutung nahe, dass die TPicture-Instanzen bereits durch die VCL freigegeben werden. Dort bitte einfach mal schauen, ob Du an der Stelle etwas durchtracen kannst (Debug-DCUs einschalten und bei TPicture.Destroy nen Breakpoint setzen). |
Scheint mir auch so. Ich gebe die Instanz jetzt manuell im FormDestory frei. Damit funktioniert es wieder. Sehr merkwürdig.  |
k, dann deckt sich das mit meiner Vermutung, dass die TPicture-Instanz von der VCL gekrallt und entsorgt wird. Kannst Du bitte einmal etwas Source geben, wie mit den TPicture-Instanzen interagiert wird?
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
jaenicke
      
Beiträge: 19339
Erhaltene Danke: 1752
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Sa 29.05.10 07:37
BenBE hat folgendes geschrieben : | | Weil nach Konvention ist eigentlich nur Create als Constructor gekennzeichnet; alles andere wäre ne Proc. Schon allein deshalb, weil der Aufruf eines Constructors vs. einer Prozedur ein klein wenig anders abläuft. |
Trotzdem kann der Konstruktor ja auch anders heißen, auch wenn man es normalerweise nicht so machen sollte. Es gibt hier auch einen Grund dafür.
In TForm gibt es Create, wobei dann auch die .dfm ausgelesen wird. Wenn es keine gibt, obwohl man eine eigene TCustomForm-Klasse abgeleitet hat (bei TForm selbst nicht), gibt es eine Fehlermeldung. CreateNew wiederum erzeugt nur das Formular, liest aber nicht die .dfm aus. Deshalb verwende ich in XStyleForm auch CreateNew.
Da es früher in Delphi kein overload gab, musste man das vermutlich anders lösen und da blieb eben nur ein anderer Name. Das dann in einer neueren Version zu ändern wäre wegen der Abwärtskompatibilität schlecht (und außerdem auch etwas undurchschaubar). Außerdem sind glaube ich die Parameter gleich, so dass man da auch nochmal ein Problem hätte.
// EDIT:
Und wegen der Freigabe:
Kann es sein, dass die TPictures schlicht einem TImage oder ähnlichem zugewiesen werden? Denn diese Objekte werden dann natürlich auch automatisch freigegeben.
|
|
freedy 
      
Beiträge: 403
Erhaltene Danke: 1
Winows 7
Delphi XE
|
Verfasst: Sa 29.05.10 10:42
jaenicke hat folgendes geschrieben : | // EDIT:
Und wegen der Freigabe:
Kann es sein, dass die TPictures schlicht einem TImage oder ähnlichem zugewiesen werden? Denn diese Objekte werden dann natürlich auch automatisch freigegeben. |
Leider nein. Ich bin auch weiter nicht dazu gekommen, weiter zu tracen. Das Picture bzw. dessen Graphic zeichne ich direkt mit GDI+ Methoden auf mein "Canvas". Keine Freigabe...
Grüße,
|
|
|