IDE vergibt unterschiedliche Adressen für dasselbe Objekt
-
Beispiel 1 (Form1.cpp)
void __fastcall TForm1::FormCreate(TObject *Sender) { foo (Label1); [...] String s = "hallo"; ShowMessage (s); }
funktioniert. Wenn ich das ändere z.B. in
Beispiel 2 (Form1.cpp)void __fastcall TForm1::FormCreate(TObject *Sender) { foo (Label1); [...] ShowMessage ("hallo"); }
kriege ich in der foo-Funktion eine AccessViolation.
Wenn ich mir im Debugger die Adressen für "Label1" und "Form1->Label1" anzeigen lassen, stellt sich heraus, dass im Beispiel 2 beide Werte eine unterschiedliche Adresse kriegen, obwohl Label1 auf Form1 enthalten ist und es eigtl dasselbe sein sollte. Bei der Dereferenzierung von Label1 in der Methode "foo" entsteht dann die Zugriffsverletzung.
In Beispiel 1 erhalten "Label1" und "Form1->Label1" dieselbe Adresse und es funktioniert wie erwartet.Kann sich das jmd erklären? Kann das an irgendeiner Einstellung liegen oder mit Speichergrößen etc.? Vor allem, was hat die untere Zeile mit dem da oben zu tun? Ich verstehe nämlich nur noch Bahnhof
-
Hi @drummi ,
nur mal schnell aus dem Bauch; ersteres ist ein String, zweites ein char array. Kanns daran liegen?
-
@drummi: Dann vergleiche mal die Adressen von
this
undForm1
.
InFormCreate
ist die globale VariableForm1
noch nicht gesetzt, (s.a.Application->CreateForm(...)
in der<Projekt>.cpp
-Datei).
Dies erklärt aber noch nicht die Zugriffsverletzung, daher zeige mal den Code vonfoo
.
-
@Helmut-Jakoby sollte nicht problematisch sein, soweit ich weiß, aber danke für den Hinweis. Es tritt hier aber tatsächlich immer dann auf, wenn ich irgendeinen Code schreibe, der ein Konstrukt wie
"hallo"
enthält. Denn immer nur dann haben this und Form1 eine unterschiedliche Adresse. Allerdings wenn ich die Zeile einfach nochmal schreibe, also z.B.:void __fastcall TForm1::FormCreate(TObject *Sender) { foo (Label1); [...] String s = "hallo"; ShowMessage (s); String t = "welt"; ShowMessage (t); }
dann funktioniert auch Beispiel 1 nicht mehr und ich kriege besagte Zugriffsverletzung.
-
@Th69:
interessant,this
undForm1
haben tatächlich nicht die gleichen Adressen im Beispiel 2. Im Beispiel 1 haben sie dieselbe Adresse. Wenn ichForm1->Label1
schreibe gibt es allerdings keine Zugriffsverletzung, zumindest nicht an der Stelle. Wenn ich nurLabel1
schreibe, hat Label1 die selbe Adresse wiethis->Label1
und es gibt die Zugriffsverletzung. Müsste es nicht andersrum sein, wenn die globale Variable Form1 noch gar nicht vorhanden ist?Ich habe auch den Eindruck, dass in FormCreate die Variablen von Form1 noch nicht gesetzt sind, kann das sein? Gibt es ein Ereignis, was danach kommt, wo alle "Startparameter" schon gesetzt sind, aber bevor das Formular gezeichnet wurde? Weil ich würde gerne Position und Größe aus ner Datei abfragen und anwenden, bevor das Formular sichtbar wird. An welche Stelle setzt man sowas am sinnvollsten hin, wenn es in der OnCreate anscheinend nicht geht?
zur foo-Funktion: Die hat an sich eigtl keine wirkliche Relevanz für das Beispiel, weil sie bisher auch immer problemlos durchgelaufen ist und seit Jahren nicht mehr verändert wurde. Aber zur Vollständigkeithalber ist das eine Funktion, die auf eine OnGuard-Komponente zugreift, die auf der Form1 liegt und einen Check durchführt. Ist ne vorgefertigte Funktion, auf die ich keinen direkten Zugriff habe und praktisch so aus dem Handbuch übernommen wurde, aber die Funktion an sich wirft ja auch keinen Fehler, sondern der Aufruf der Objektadresse.
void foo (TOgRegistrationCode*& OgRegGuard) { try { OgRegGuard->CheckCode(true); /*HIER wird die Zugriffsverletzung geworfen, beim Zugriff auf die Adresse von OgRegGuard (read of address ...), allerdings landet es nicht im catch, sondern das Programm stürzt ab.*/ } catch (...) { throw std::runtime_error ("Prüfung fehlgeschlagen."); } }
-
Hallo @drummi ,
etwas verwirrend, aber das klingt irgendwie danach, dassTForm1
noch nicht fertig instanziiert ist. Ich kenne VCL nicht, deshalb kann ich nur raten. Gibt es eine Oberklasse vonTForm1
? Und wenn, hat die auch eine MethodeFormCtreate(...)
? Wird diese auch aufgerufen? Wo wird eigentlichLabel1
initialisiert? Scheint ja noch nicht erstellt worden sein. Zumindest crasht wohl infoo(...)
der Zugriff darauf.
-
@drummi sagte in IDE vergibt unterschiedliche Adressen für dasselbe Objekt:
interessant,
this
undForm1
haben tatächlich nicht die gleichen Adressen im Beispiel 2. Im Beispiel 1 haben sie dieselbe Adresse.Hab zwar auch keine VCL-Erfahrung, aber ne Idee zum rumprobieren: Schau mal ob sich was ändert, wenn du sicherstellst, dass
TForm1
eine polymorphe Klasse ist (falls das nicht schon eh der fall ist):class TForm1 { ... virtual ~TForm() = default; ... };
Interessant wäre auch zu sehen, wie
TForm1::FormCreate
aufgerufen wird. Vielleicht mir irgendeinem komischen Cast oder sowas. Das ist aber möglicherweise irgendwo im VCL-Code vergraben. Wennthis
nicht die richtige Adresse hat, kann das eben auch an einem Aufruf á lakomischerpointer->FormCreate(...)
liegen.Dennoch höchst merkwürdig, weil es scheinbar erst dann passiert, wenn du in der Funktion etwas zusätzlich auf dem Stack erstellst (sehr unwahrscheinlich) oder wenn ein neues konstantes String-Literal eingefügt wird.
Bei letzterem hätte ich sogar tatsächliche ne vage Spekulation: Je nachdem wo und in welcher Sektion in der ausführbaren Datei konstante Strings und VTables landen, könnte ein extra String-Literal tatsächlich die absolute Position des VTables von
TForm1
"verschieben". Das ist aber eine ganz normale Linker-Aufgabe sowas richtig zusammenzustoppeln, daher auch nur wilde Spekulation - teste aber dennoch das mit der polymorphen Klasse mal.
-
@drummi: Hast du evtl. aus Versehen
OldCreateOrder = true
gesetzt? (s.a. OldCreateOrder – er, what?)
Dann ändert sich die Reihenfolge der Initialisierung.
Gerade noch mal nachgeschaut: meine alten BCB-Programme greifen auch inFormCreate
auf die Controls zu (FormCreate
ist eine Ereignismethode, welche man im IDE-Designer zuweist).@Finnegan: Jede (von der IDE) erstellte Form-Klasse erbt von
TForm
und hat automatisch einen virtuellen Destruktor.Leider scheint (seit mindestens gestern) die gesamte Produkt-Dokumentation für RADStudio 12 (Athens) nicht mehr zu funktionieren (ich erhalte dort nur entweder leere Seiten oder aber eine Fehlermeldung bzgl. Datenbank-Zugriff)...
-
Schonmal vielen Dank für eure ganzen Ideen!
@Helmut-Jakoby : genau, es gibt die
TForm
als Oberklasse, die von diversen (TCustomForm
bis zuTObject
) abgeleitet ist. Wird aber beim Anlegen von der IDE automatisch "kreiert". Ist eine gute Frage woLabel1
tatsächlich initialisiert wird, da das der Designer beim Anlegen ebenfalls automatisch schreibt. In der Header-Datei taucht die Deklaration des Variablennamens als Pointer impublished
Bereich auf und das müsste dann, soweit ich das verstehe, durch den Konstruktor letztlich initialisiert werden.@Finnegan: wie Th69 schon sagte, hat der automatisch nen virtuellen Konstruktor. Habe spaßeshalber trotzdem mal ausprobiert den nochmal manuell zu überschreiben, aber das ändert nichts an dem Verhalten. In der
main{}
-Funktion wird durchApplication->CreateForm(__classid(TForm1), &Form1)
der Konstruktor aufgerufen und erst anschließend dasOnCreate
-Ereignis aufgerufen. Deswegen hätte ich auch gedacht, dass dort alle Variablen bereits initialisiert wurden. Laut folgendem Beitrag passiert das auch (bezieht sich zwar auf Delphi, aber sollte hier genauso funktionieren):Dadurch das aber bei CreateForm erst der Speicher angefordert und der Variablen zugewiesen wird, und dann erst der Constructor aufgerufen wird kann man innerhalb des Constructors bereits auf die Variable zugreifen.
Quelle: https://www.delphipraxis.net/103915-geloest-application-createform.htmlWoanders liest man hingegen:
This article speaks about the dangers of accessing the form instance in its own OnCreate event handler. One way to avoid this is to refrain to doing any initialization in main form's OnCreate handler. Wait until main form is truly ready. For this send a message to your self.
Quelle: https://stackoverflow.com/questions/29786757/application-createform-forms-visibility-stateLeider ist der Artikel nicht mehr verfügbar. Aber es scheint, dass man die OnCreate Funktion eigtl gar nicht nutzen sollte und ich das bisher immer falsch gemacht habe ...
@Th69 : Meinst du, es wäre sinnvoll, in der
main
-Funktion zwischenApplication->CreateForm(...)
undApplication->Run()
neInit()
-Funktion einzubauen, die man durch ne externe Unit inkludiert? Und dass ich da den ganzen Kram aus dem OnCreate erst reinschreibe? An der Stelle müsste dann ja spätestens alles bekannt und korrekt initialisiert sein. Oder ist es klüger, so wie oben vorgeschlagen am Ende vonOnCreate
einePostMessage
zu senden und darauf zu reagieren?An
OldCreateOrder
hatte ich auch schon gedacht, aber nö, die steht auffalse
. Hab's auch mal auf true und wieder zurückgesetzt und alles neu compiliert, aber macht keinen Unterschied. Habe heute Morgen auch mal mit den Optionen'this' zuerst
undVTable nach vorn
rumgetestet, aber gleiches Ergebnis. Also mittlerweile scheint es mir, dass @Finnegan mit seiner Spekulation Recht haben könnte und da beim Compilen/Linken Murks passiert (was ja bei Emba nicht selten ist...) oder dass das generell einer der Gründe ist, warum man die OnCreate nicht benutzen soll. Warum es sie dann gibt ist mir dann allerdings schleierhaft. Und das sind eh alles schon wieder Probleme, mit denen ich mich überhaupt nicht rumschlagen wollte und ziemlich frustrierend sindDie Doku-Seite ist derzeit nicht verfügbar, weil die nen Hardware-Ausfall im Rechenzentrum haben (ich benutze allerdings auch noch Tokyo): https://blogs.embarcadero.com/we-are-experiencing-a-hardware-outage/
-
Setz' doch mal ein Testprojekt auf und schau, ob das dort funktioniert.
Ich denke, irgendwas ist an deinem jetzigen Projekt falsch eingestellt.Ansonsten kannst du eines der anderen Ereignisse (
OnShow
,OnActivate
) verwenden: Creating and Closing Forms - evtl. mit einembool
-Flag für nur einmaligen Aufruf deiner Funktionen.PS: Danke für den Link bzgl. Hardware-Ausfall bei Embarcadero.
-
@Th69 werde ich mal probieren, danke. Beim Stöbern in der BCB-Hilfe habe ich noch einen Hinweis entdeckt, der besagt, dass man die FormCreate nur in Delphi verwenden solle, aber nicht in C++, da da irgendwelche Verbindungen zum Konstruktor nicht funktionieren würden (die genaue Formulierung hab ich nicht mehr im Kopf) ... vllt auch ne Erklärung. Allerdings machen die ja selbst Beispiele in FormCreate in C++, naja...
-
Ich habe gelernt, bei C++ Konstruktor und Destruktor anstelle von
OnCreate
undOnDestroy
zu verwenden. Die Embarcadero-Hilfeseite ist leider immer noch offline, bei Stackoverflow findet man diesbezüglich aber Hinweise anerkannter Experten.C++ Builder Lifetime of Static Class Property/Variable of a dynamicly created form?
"That being said, you should not be using the TForm::OnDestroy (or OnCreate) event in C++ to begin with. They are Delphi idioms that can cause undefined behavior in C++, as Delphi and C++ use different object creation/cleanup semantics. The OnDestroy event can occur after your C++ destructor (and the OnCreate event can occur before your C++ constructor). So, in C++, always use your TForm's actual destructor (and constructor) instead."Deleting dynamically created component?
"On a side note: you should NOT use the Form's OnCreate and OnDestroy events in C++Builder. They are a Delphi idiom that can lead to undefined behavior in C++ if you are not careful. OnCreate can fire before your Form's constructor, and OnDestroy can fire after your Form's destructor. As such, you should use the Form's actual constructor and destructor instead."