Autor Beitrag
freedy
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 403
Erhaltene Danke: 1

Winows 7
Delphi XE
BeitragVerfasst: 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:
  1. In der Komponente gibt es mehrere Instanzen von TPicture als Property (published).
  2. Um PNGs darzustellen, nutze ich das PNGImage von Gustavo Huffenbacher Daud SourceForge (leider down)
  3. 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
ausblenden 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
ausblenden 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:
ausblenden 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 <> nilthen
  begin
    FPicture.OnChange := nil;
    FPicture.OnProgress := nil;
    FreeAndNil(FPicture);
  end;

  if (FHotPicture <> nilthen
  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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 403
Erhaltene Danke: 1

Winows 7
Delphi XE
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 403
Erhaltene Danke: 1

Winows 7
Delphi XE
BeitragVerfasst: Do 27.05.10 17:40 
user profile iconBenBE hat folgendes geschrieben Zum zitierten Posting springen:
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.

user profile iconBenBE hat folgendes geschrieben Zum zitierten Posting springen:
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:

ausblenden 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.

user profile iconBenBE hat folgendes geschrieben Zum zitierten Posting springen:
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.

user profile iconBenBE hat folgendes geschrieben Zum zitierten Posting springen:
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.

user profile iconBenBE hat folgendes geschrieben Zum zitierten Posting springen:
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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: Do 27.05.10 18:07 
user profile iconfreedy hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconBenBE hat folgendes geschrieben Zum zitierten Posting springen:
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.

user profile iconfreedy hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconBenBE hat folgendes geschrieben Zum zitierten Posting springen:
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:

ausblenden 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.

user profile iconfreedy hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconBenBE hat folgendes geschrieben Zum zitierten Posting springen:
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.

user profile iconfreedy hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconBenBE hat folgendes geschrieben Zum zitierten Posting springen:
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.

user profile iconfreedy hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconBenBE hat folgendes geschrieben Zum zitierten Posting springen:
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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19339
Erhaltene Danke: 1752

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Sa 29.05.10 07:37 
user profile iconBenBE hat folgendes geschrieben Zum zitierten Posting springen:
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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 403
Erhaltene Danke: 1

Winows 7
Delphi XE
BeitragVerfasst: Sa 29.05.10 10:42 
user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
// 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,