Komponentenentwicklung: Zeiger auf andere Komponente
-
Bei meiner selbstentwickelten Komponente ist eine Eigenschaft ein Zeiger auf eine andere
Komponente, (die als Datenquelle fr diese Komponente wirkt). Wenn ich im Formulardesigner jene Datenquellenkomponente loesche, erhalte ich staendig die Exceptionmeldung des BorlandBuilders(5.0):"Zugriffsverletzung bei Adresse 40004B3C in Modul VCL50.BPL.
Lesen von Adresse FFFFFFFF."woraus ich mich kaum noch befreien kann.
Bei Eigenschaften der Borlandkomponenten, z.B. DataSet von TDataSource passiert das nicht: Wenn da die Komponente, auf die verwiesen wird, geloescht wurde, wird einfach auch die Eigenschaft auf NULL gesetzt.
Warum klappt das hier nicht? Muss ich einen eigenen Eigenschaftseditor von TComponentProperty ableiten und die Methode Edit mit einer Exceptionbehandlung fuer eAccessviolation umklammern oder gibt es auch eine einfachere Moeglichkeit?
Danke Heiner
-
Hi,
zeig mal den Konstruktor der klasse sowie die Geter und Seter- Methoden der Eigenschaft.
Irgenwas machst du falsch..
-
Schau Dir doch mal Notification und FreeNotification etc. an.
In Notification ist es möglich, sich davon unterrichten zu lassen,
wenn eine andere Komponente gelöscht wird.Gruß,
Alexander
-
Alexander Kempf schrieb:
In Notification ist es möglich, sich davon unterrichten zu lassen, wenn eine andere Komponente gelöscht wird.
Danach wird hier aber nicht gefragt.
-
WebFritzi schrieb:
Alexander Kempf schrieb:
In Notification ist es möglich, sich davon unterrichten zu lassen, wenn eine andere Komponente gelöscht wird.
Danach wird hier aber nicht gefragt.
Sondern?
Wenn ich mir den allerersten Beitrag anschaue, dann komme ich eigentlich zu
dem Schluß.
Was glaubst Du denn, was gefragt wurde?
Vielleicht kann sich ja auch mal Heiner dazu äußern.Gruß,
Alexander
-
Von Heiner.
hier ist der Code://Deklaration der Komponente TPVEdit (Auszug): class PACKAGE TPVEdit : public TCustomEdit { private: TPV *FPV; // Zeiger auf Datenquelle (jene böse...) public: __fastcall TPVEdit(TComponent* Owner); __published: __property TPV* PV = {read= GetPV, write = SetPV,default=NULL}; }; //--------------------------------------------------------------------------- //Elementfunktionen GetPV, SetPV. Urspruenglich waren beide Funktionen einfache //Lese- und Schreibfunktionen von/zu FPV. Aber das klappte auch nicht. TPV* __fastcall TPVEdit::GetPV() { void* falseptr = (void*)0xFFFFFFFF; TPV* value; try { if ((void*)FPV == falseptr) // verzweifelter Versuch, un- // gueltige Zeiger zu value = NULL; // loeschen, klappt aber nicht else value = FPV; } catch(const EAccessViolation &e) { FPV= NULL; return NULL; } // Ende catch return value; } //-------------------------------------------------------------------------- void __fastcall TPVEdit::SetPV(TPV* pv) { void* falseptr = (void*)0xFFFFFFFF; try { if ((void*)pv == falseptr) FPV = NULL; else if (pv->ClassNameIs("TPV")) FPV = pv; else FPV = NULL; } catch(const EAccessViolation &e) { FPV= NULL; return; } // Ende catch }
Edit:
Bitte die Code-Tags benutzen. Danke!
-
Hallo Heiner,
zuerst würde ich das ganze "falseptr"-Zeug rausschmeißen. Ich glaube, das lenkt nur vom wesentlichen ab.
Initialisierst Du die private Variable "FPV" im Konstruktor mit NULL ?
Und hast Du mal versucht, die Komponente in einem Testprogramm zur Laufzeit zu erzeugen? Wenn der Fehler da auch auftritt, könntest Du wenigstens debuggen...
-
Hi,
erst einmal musst du die Variable FPV initialisieren.
Schreib im Konstruktor :
FPV=NULL;
in der Methode GetPV()
ist es egal ob die Variable NULL ist. Warum den Aufwand treiben um festzustellen ob die Variable NULL ist und dann NULL zurückgeben ? unnütz.schreib einfach :
return FPV;
in der Methode SetPV(TPV* pv) das selebe Spiel. Warum feststellen ob pv NULL ist und dann der Variablen FPV NULL zuweisen ?
schreib einfach :
FPV=PV;
wenn du innerhalb der Klasse mit dieser Variable arbeiten möchtest musst du immer prüfen, ob diese Variable auch erstellt wurde und nicht NULL ist.
So zum Beispiel:if(FPV != NULL) { // Machwas }
die Umwege über void- Zeiger kannst du dir sparen.
-
Danke fuer alle Tipps. Mit "Notification() kam ich auf die richtige Spur.
Sorry, dass ich die Tags wieder nicht richtig verwendet habe. Schau spaeter nach, wie man das macht.
HeinerUeberschreibung der Elementfunktion Notification().
Wird eine Komponente im Formulardesigner oder waehrend der Laufzeit geloescht, sendet der Destruktor dieser Komponente eine opRemove-Nachricht an alle anderen Komponenten desselben Formulars. Der Aufruf von Notification() ist die Reaktion auf den Empfang einer solchen Botschaft. Standardmaessig leitet Notification() die Botschaft an alle untergeordneten Komponenten weiter. Im vorliegenden Fall muss Notification() fuer TMyComponent ueberschrieben werden://Deklaration der Komponente TMyComponent: class TOtherComponent; // Klasse der anderen Komponente, worauf die // Eigenschaft in MyComponent zeigt class PACKAGE TMyComponent : public TComponent { private: TOtherComponent *FOtherComp; // Zeiger auf andere Komponente protected: virtual void __fastcall Notification(Classes::TComponent* AComponent, Classes::TOperation Operation); // Deklaration der ueberschriebenen Methode Notification public: __fastcall TMyComponent(TComponent* Owner); __published: __property TOtherComponent* OtherComp = {read= FOtherComp, write = FOtherComp,default=NULL}; }; //------------------------------------------------------------------------- // Die Elementfunktion Notification: void __fastcall TMyComponent::Notification(Classes::TComponent* AComponent, Classes::TOperation Operation) { TComponent:: Notification(AComponent,Operation); // Aufruf von Notification() der Basisklasse if(Operation == opRemove) { if (AComponent == OtherComp) { OtherComp = NULL; } } // Ende "if(Operation == opRemove)" } // Ende Notification() //---------------------------------------------------------------------------
Falls sich OtherComp in einem anderen Formular derselben Anwendung befinden kann, muss Vorsorge getroffen werden, dass die opRemove-Nachricht auch MyComponent erreicht. Das kann mit FreeNotification geschehen, z.B. durch Ueberschreiben der Elementfunktion BeforeDestruction() der Klasse TOtherComponent :
// Deklaration: class PACKAGE TOtherComponent : public TComponent { ... protected: virtual void __fastcall BeforeDestruction(void); // wird aufgerufen, wenn die Komponente geloescht public: __fastcall TOtherComponent(TComponent* Owner); //Konstruktor __published: ... }; //------------------------------------------------------------------------- void __fastcall TOtherComponent::BeforeDestruction(void) // wird aufgerufen, wenn Instanz von TOtherComponent geloescht werden soll { TComponent::BeforeDestruction(); // zuerst geerbte Methode aufrufen /* Benachrichtigung ("Notification") aller Komponenten in anderen Formularen der Anwendung, dass Instanz von TOtherComponent geloescht wird. Zuerst als obersten "Owner" TApplication suchen; dann dessen Methode FreeNotification() aufrufen. */ TComponent* Firstowner = this->Owner; if(!FirstOwner) return; // Komponente ohne Besitzer, kann nicht benachrichtigen while ( !FirstOwner->ClassNameIs("TApplication")) { FirstOwner = owner->Owner; // eine "Besitzeretage" hoeher if (FirstOwner==NULL) return; // TApplication nicht gefunden } FirstOwner->FreeNotification(this); // Botschaft versenden } //---------------------------------------------------------------------------
Bei mir funktioniert das jetzt sowohl im Formulardesigner als auch
zur Laufzeit. Bei Fragen oder Korrekturen dazu bin ich per eMail zu erreichen: Heiner.Kemmann@t-online.de. Vielleicht kann man dieses Problem in die FAQ uebernehmen.
Freundliche Gruesse Heiner[EDIT] CodeTags eingefügt [/EDIT]