RemoveComponent() funktioniert nicht.
-
Hallo zusammen,
ich will mit RemoveComponent() zur Laufzeit Komponenten vom Formular löschen.
Laut Hilfe reicht RemoveComponent(). In FAQ's wird öfters zusätzlich ein Komponente->Free() empfohlen.
Es funktioniert aber alles nicht so, wie es soll. Ich kann ein paar Komponenten scheinbar korrekt löschen, aber irgendwann gibt es beim Löschen eine Exception.
Genaugenommen möchte ich ein TImage, das auf einem TPanel sitzt löschen durch Drag&Drop auf einen Mülleimer:
TImage* TheImage; if (TheImage = dynamic_cast<TImage*>(Source)){ TPanel* ThePanel = (TPanel*)(TheImage->Parent); TComponent* pC = FindComponent(TheImage->Name); TComponent* pC2 = FindComponent(ThePanel->Name); if (pC != NULL) pC->Owner->RemoveComponent((pC)); TheImage->Free(); TheImage = NULL; if (pC2 != NULL) pC2->Owner->RemoveComponent((pC2)); ThePanel->Free(); ThePanel = NULL; }
Alle Beiträge, die ich gefunden habe, haben das Problem auch nicht wirklich gelöst.
Hat jemand eine Idee?Danke im Voraus,
Martin
-
Hallo
Weder RemoveComponent noch Free solltest du in C++ einsetzen. Alles was du brauchst ist delete.TImage* TheImage; if (TheImage = dynamic_cast<TImage*>(Source)) { TPanel* ThePanel = (TPanel*)(TheImage->Parent); // delete TheImage; // Brauchst du nur, wenn der Owner von Image nicht das Panel selber ist delete ThePanel; }
Beachte das du nur dynamisch erstellte Instanzen auch dynamisch löschen solltest.
bis bald
akari
-
Hallo akari,
danke erst mal für deine Antwort.
Aber ein einfaches delete führt bei mir direkt im Anschluss zu einer Access violation. Nur RemoveControl (ohne delete) funktioniert zwar prima, jedoch wird der Speicher nicht freigegeben. Da es sich um Bilder handelt und die Anwendung lange Zeit laufen soll, ist das aber wichtig.Ich erklär mal etwas genauer wie ich es mache:
Auf einem TForm liegt ein TPanel als Container. In diesem Container werden mehrere Panels abgelegt, die jeweils ein TImage beinhalten.
Nun erstelle ich zur Laufzeit ein solches Panel/Image Paar:
TPanel* PanelToAdd = new TPanel(PanelUnten); // PanelUnten ist der oben genannte Container PanelToAdd->Parent = PanelUnten; TImage* ImageToAdd = new TImage(PanelToAdd); ImageToAdd->Parent = PanelToAdd;
Wenn ich das zur Entwurfszeit visuell mache, dann ist der Owner zwar immer das Formular, aber das ändert auch nichts.
In meiner DragDrop Funktion ist nun der Parameter Source das Image. Also ermittle ich zum Image auch das Panel auf dem das Image liegt:
TImage* TheImage; if (TheImage = dynamic_cast<TImage*>(Source)){ TPanel* ThePanel = (TPanel*)(TheImage->Parent); delete TheImage; delete ThePanel; }
Es scheint als greift die Anwendung nach dem delete noch auf die Komponenten zu, dabei dann die Zugriffsverletzung.
Ein einfaches delete entfernt doch auch nicht die Komponente aus dem Control-Array des owners - oder?In der FAQ ist ein sehr ähliches Thema, an dem du damals auch beteiligt warst. Da scheint das delete funktioniert zu haben, aber ich sehe nicht, was bei mir anders sein soll.
Spielt die Version eine Rolle? Ich arbeite mit einer etwas älteren Version BCB 5.0.
Es wäre echt super, wenn da jemand helfen könnte - ich bin ja bestimmt nicht der erste, der eine Komponente vom Formular löschen will. Die Beispiele in der Hilfe verschieben die Komponenten immer mit RemoveControl/InsertControl. Dort wird leider nie gelöscht.
Gruß
Martin
-
Hallo
Ein einfaches delete entfernt doch auch nicht die Komponente aus dem Control-Array des owners - oder?
Doch. Das macht der Destruktor, der von delete aufgerufen wird.
Es scheint als greift die Anwendung nach dem delete noch auf die Komponenten zu, dabei dann die Zugriffsverletzung.
...
In der FAQ ist ein sehr ähliches Thema, an dem du damals auch beteiligt warst. Da scheint das delete funktioniert zu haben, aber ich sehe nicht, was bei mir anders sein soll.Sowohl mein hier als auch in der FAQ gezeigtes Prinzip funktioniert, auch auf dem BCB 5. Bei dir ist noch etwas anders in deinem Quellcode, das die Zugriffsverletzung verursacht. Das läßt sich aber für mich hier aus deinem Auszug nicht erkennen. Wenn du mal zum Test ein neues Projekt erstellst, in dem du nur zwei Buttons erstellst, bei dem einer eine Komponente erstellt und der andere diese wieder löscht, wirst du sehen das es funktioniert.
Möglicherweise werden noch nach dem Löschen ein Event ausgelöst, der auf das mittlerweile gelöschte Control zugreifen will. Benutz den Debugger um die genaue Stelle zu finden, die die Zugriffsverletzung auslöst. Ich bin mir sicher die Ursache liegt in deinem Code, nicht in der VCL.
bis bald
akari
-
Hallo akari,
stimmt - wenn ich es von einem anderen Button aus lösche, dann funktioniert es.
Da ich sonst nichts Spezielles am laufen habe, kann es eigentlich nur daran liegen, dass das Image am Drag&Drop beteiligt ist. Das Drag&Drop wurde mit Image->BeginDrag(... gestartet. Das zu löschende Image ist der Source Parameter in der DragDrop Funktion des "Mülleimers"
Bei Debuggen sehe ich, dass die Fehlermeldung nicht im Moment des Löschens selbst geschieht, sondern erst wenn die DragDrop Funktion verlassen wird. Ich nehme an, um das Drag&Drop abzuschließen wird intern noch irgendwo auf das Image zugegriffen. Ich habe auch schon versucht, das Drag&Drop vor dem Löschen selbst zu beenden: Image->EndDrag(... aber das ändert nichts an der Fehlermeldung welche lautet: "Abstract Error"
Gruß
Martin
-
Hallo,
nun will ich doch kurz berichten wie ich das Problem "gelöst" habe.
Beim Debuggen habe ich gesehen, dass in der VCL (wie bereits vermutet) noch einiges erledigt wird um das Drag&Drop abzuschließen nachdem die DragDrop Funktion verlassen wird. Dabei wird auch nochmals auf das Objekt zugegriffen, welches mit BeginDrag() das Drag&Drop ausgelöst hat, also genau dasjenige, welches ich gerne löschen will. Wird es zu früh gelöscht, dann ist die Zugriffsverletzung die plausible Folge.
Ich entferne nun das Objekt lediglich mit RemoveComponent, trage es in eine Liste ein mit Objekten, die gelöscht werden sollen. Diese wird im Idle-Handler abgearbeitet und so der Speicher wieder frei gegeben. Nach dem Motto: dann lösch es eben später wenn gerade nix zu tun ist.
So funktioniert es jedenfalls.
Wenn jemand eine bessere Idee hat, würde mich das natürlich interessiern.Gruß
Martin
-
Definiere eine eigene Message
Statt löschen mach damit ein PostMessage an Hauptform
Im Handler zur Message kannst Du problemlos löschenGruss
FrankPS: Nimm KEIN SendMessage !!!