Visual Studio erlaubt keine Zeiger auf eine C++/CLI-Verweisklasse!

OP #7446110
Lesenswert?

Ich versuche ein Programm von C++ Builder auf Microsoft Visual Studio zu überschreiben.

Das läuft in C++ Builder ohne Fehler: VarBesch(&i,z,0,VarInfo,"[Form->MinimumPixelE→Text]", (DWORD)Form1->MinimumPixelE,EDITEXT);

Im Visual Studio Microsoft: VarBesch(&i, z, 0, VarInfo, "[MinimumPixelE->Text]",(DWORD)this->MinimumPixelE->Text, EDITEXT);

bringt Fehler: Ungültige Typkonvertierung. "Typumwandlung": "System::String ^" kann nicht in "DWORD" konvertiert werden.

Weitere Test haben das ergeben: System::Windows::Forms::CheckBox *gg; //← Fehler! gg = this→KreisZu; E1986: ein normaler Zeiger auf eine C++/CLI-Verweisklasse oder -Schnittstellenklasse ist nicht zulässig.

Also: C++/CLI erlaubt „keine normale Zeiger auf eine C++/CLI-Verweisklasse oder -Schnittstellenklasse“ Gibts es auch „nicht normalen Zeiger“... ? Das Bedeutet dass Visual Studio keine Zeiger auf eine C++/CLI-Verweisklasse erlaubt! Ich habe eine Frage an Microsoft Entwickler:

  1. Passiert das, dass anders technisch nicht möglich wäre.
  2. Oder absichtlich wurde die Begrenzung eingeführt mit der   Begründung:„sicheres Programmieren". Veganen essen kein Fleisch und das geht mich nicht an. Aber manche Veganen wollen anderen Menschen das Essen von Fleisch (auch Milchprodukten…) verbieten! C++ Builder hat keine solchen Begrenzungen und nach vielen Jahren ist weiter beliebt und effektiv! In 80. und 90. Jahren 20. Jahrhundert kamen Schulabsolventen In Betrieben mit Pascal kenntnissen. Professoren in Schulen haben nur Pascal gelernt… In Betrieben mussten Sie aber in „C“ programmieren. Pointer Akrobatik war in Softentwicklung notwendig. Pascal hat mit „C“ verloren!
OP #7446130
Lesenswert?

Irgend W. schrieb:

  • Wie sind die Parameter von "VarBesch(...)" deklariert?
  • Von welchem Type ist "Form1->MinimumPixelE"?
  • Von welchem Type ist "this->MinimumPixelE->Text"?

Das it nicht wichtig. Das habe im Grrif. Das ist wichtig: Weitere Test haben das ergeben: System::Windows::Forms::CheckBox *gg; //← Fehler! gg = this→KreisZu; E1986: ein normaler Zeiger auf eine C++/CLI-Verweisklasse oder Schnittstellenklasse ist nicht zulässig.

#7446131
Lesenswert?

B. P. schrieb:

Ich versuche ein Programm von C++ Builder auf Microsoft Visual Studio zu überschreiben.

Das läuft in C++ Builder ohne Fehler: VarBesch(&i,z,0,VarInfo,"[Form->MinimumPixelE→Text]", (DWORD)Form1->MinimumPixelE,EDITEXT); System::Windows::Forms::CheckBox *gg; //← Fehler! gg = this→KreisZu; E1986: ein normaler Zeiger auf eine C++/CLI-Verweisklasse oder -Schnittstellenklasse ist nicht zulässig.

Also: C++/CLI erlaubt „keine normale Zeiger auf eine C++/CLI-Verweisklasse oder -Schnittstellenklasse“ Gibts es auch „nicht normalen Zeiger“... ?

Ja, nennt sich sharedPointer (smartpointer) und der enthält den "normalen" Pointer bzw eine Referenz auf den String und kann ihn löschen wenn der Referenzzähler auf 0 ist). -> https://en.cppreference.com/w/cpp/memory/shared_ptr

#7446140
Lesenswert?

B. P. schrieb:

VarBesch(&i, z, 0, VarInfo, "[MinimumPixelE->Text]",(DWORD)this->MinimumPixelE->Text, EDITEXT);

bringt Fehler: Ungültige Typkonvertierung. "Typumwandlung": "System::String ^" kann nicht in "DWORD" konvertiert

Mag es sein, dass DWORD als 32-Bit-Wert definiert ist, nun aber das ganze für ein 64-Bit-Target compiliert wird und der Zeiger viel zu groß ist?

B. P. schrieb:

Gibts es auch „nicht normalen Zeiger“... ?

Seit C++11, freilich: std::shared_ptr<>, std::weak_ptr<>, std::unique_ptr<>. Letztere sind vom Footrprint her das Pendant zu Raw-Pointern.

[C++/CLI-Verweisklasse]

Die sind, soweit ich das überblicke, ein Brückenschlag zwischen C++ und den gemanagten Ressourcen von dot-NET. Dass die zu diesem Zweck auf C++-Seite auch mit gemanagten Zeigern betan werden wollen, ist irgendwie einleuchtend.

B. P. schrieb:

Aber manche Veganen wollen anderen Menschen das Essen von Fleisch (auch Milchprodukten…) verbieten!

[MSVC-C++/CLI] ... wer in ein veganes Restaurant geht, kann sich schwerlich darüber aufregen, dass dort der Wunsch nach Fleisch auf Ablehnung stößt. Letzteres gibt es dann im Steakhouse.

HTH (re)

#7446145
Lesenswert?

Was ist denn eine "Verweisklasse"?

Re schrieb:

B. P. schrieb:

Aber manche Veganen wollen anderen Menschen das Essen von Fleisch (auch Milchprodukten…) verbieten!

[MSVC-C++/CLI] ... wer in ein veganes Restaurant geht, kann sich schwerlich darüber aufregen, dass dort der Wunsch nach Fleisch auf Ablehnung stößt. Letzteres gibt es dann im Steakhouse.

Mit anderen Worten: Wenn dir die Einschränkungen, die dir das .net-Zeugs mit "C++/CLI" auferlegt, nicht magst, dann nimm halt echtes C++.

OP #7446231
Lesenswert?

Hans-Georg L. schrieb:

Ja, nennt sich sharedPointer (smartpointer) und der enthält den "normalen" Pointer bzw eine Referenz auf den String und kann ihn löschen wenn der Referenzzähler auf 0 ist). -> https://en.cppreference.com/w/cpp/memory/shared_ptr

Zitat (Auszug...)vom pdf-Buch von Prof.Richard Kaiser: "int* p=0; // p zeigt nicht auf einen reservierten // Speicherbereich Bei einem Handle muss man dafür nullptr verwenden: int^ h=nullptr; // h zeigt auf keinen Wert"

Das bringt schon keinen Fehler:

System::Windows::Forms::CheckBox^ x = nullptr;

Also einen nullptr kann ich definieren. Was kann man damit weiter machen...dann... sehe ich noch...

#7446282
Lesenswert?

Würdest Du den C++-Compiler verwenden, hättest Du das Problem nicht. C++/CLI ist /kein/ C++, sondern eine Microsoft-Perversion für das .Net-Geraffel.

Visual Studio kommt aber auch mit einem richtigen, nativen C++-Compiler, und der ist a) standardkonform, b) ziemlich aktuell und kann c) echte C++-Programme übersetzen.

#7446370
Lesenswert?

Harald K. schrieb:

Würdest Du den C++-Compiler verwenden, hättest Du das Problem nicht. C++/CLI ist /kein/ C++, sondern eine Microsoft-Perversion für das .Net-Geraffel. Visual Studio kommt aber auch mit einem richtigen, nativen C++-Compiler, und der ist a) standardkonform, b) ziemlich aktuell und kann c) echte C++-Programme übersetzen.

Dann gibt's aber kein Windows Forms, und die MFC ist nicht in der kostenlosen Version drin. Was spricht gegen natives C#? Geschwindigkeit? Da könnte man den kritischen Code immer noch in C++ schreiben und als DLL einbinden.

#7446424
Lesenswert?

Christian R. schrieb:

Dann gibt's aber kein Windows Forms, und die MFC ist nicht in der kostenlosen Version drin.

Dann nimmt man halt was anderes; wxWidgets ist in der Struktur der MFC ähnlich und hat den Vorteil, sogar auf anderen Betriebssystemen nutzbar zu sein. Keine Abhängigkeit von einer nur von Microsoft unterstützten Sprache und/oder Entwicklungsumgebung.

Warum eigentlich musste der C++-Builder verlassen und das Projekt "portiert" werden?

Christian R. schrieb:

Was spricht gegen natives C#?

Schon wieder 'ne andere Programmiersprache, und trotzdem mit dem .Net-Geraffel als Unterbau. Warum dann nicht gleich VB.net nutzen?

OP #7446464
Lesenswert?

using namespace System; using namespace System::Windows::Forms;

Zitaten:

Namespaces werden verwendet, um Code in logischen Gruppen zu organisieren und Namenskonflikte zu vermeiden, die insbesondere dann auftreten können, wenn die Codebasis mehrere Bibliotheken enthält. Alle Bezeichner im Gültigkeitsbereich des Namespaces sind ohne Qualifizierung füreinander sichtbar.

Die Variante mit using namespace sollte üblicherweise nur innerhalb von Funktionen genutzt werden, da bei Verwendung im globalen Namensraum das ganze Konzept der Namensräume obsolet wird. In gedruckten Büchern wird es gern global verwendet, da dies etwas Platz spart und der in solchen Büchern knapp ist.

"using namespace std" braucht man, um das Namespace-Konzept von C++ ad-absurdum zu führen. Namespaces dienen dazu, Namensbereiche zu trennen. Mit diesem unsäglichen "using namespace" wird genau das wieder aufgehoben. Oder anders ausgedrückt: Laßt es einfach weg und schreibt std::string, std::cout, std::endl usw.

using namespace ? könnte jemand das logisch erklären...

Angehängte Dateien:
OP #7446491
Lesenswert?

Zitat: Da nicht auszuschließen ist, dass zwei unabhängig voneinander entstandene Quelltexte die gleichen Namen verwenden, wurde in C++ der Namensraum eingeführt. Damit können Deklarationen und Definitionen unter einem Namen zusammengefasst und gegen andere Namen abgegrenzt werden.

OP #7446543
Lesenswert?

Das klingt vernünftig: macht all das, was im Namensraum enthalten ist und durch Header bereits eingebunden wurde, im aktuellen Namensraum bekannt. ( Funktionen, Typen, ... )

In meinem Entwurf von Forms sehe ich keine #include von diesem System...: namespace $safeprojectname$ { using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; Das müsste auf einer anderen Stelle (für mich nicht sichtbar passieren...) Das namespace ist eine Art von Erinnerung...?

#7446544
Lesenswert?

B. P. schrieb:

using namespace ? könnte jemand das logisch erklären...

Ein Zweck von "using namespace" ist u.a., den Bestand an qualifiziertem (oder sogar zertifiziertem) Legacy-Code, bei denen die Funktionen der Standard-Library oder anderen Bibliothenken noch nicht durch Namensräume getrennt gewesen waren, ohne umfangreiche Änderungen mit modernem C++ weiterbenutzen zu können,

Dass man in neu geschriebenen oder refaktorierten Code-Zeilen mit solchen Mechanismen sehr sparsam umgehen sollte, versteht sich wohl von selbst.

Kennst Du https://www.stroustrup.com/tour3.html ? Da werden viele Deiner Fragen beantwortet.

B. P. schrieb:

"using namespace std" braucht man, um das Namespace-Konzept von C++

ad-absurdum zu führen.

Das halte ich für maßlos übertrieben. Ja, man sollte es vermeiden. Aber Namensräume, die nicht per "using" eingebunden werden, bleiben ja weiterhin gekapselt. Nix "ad-absurdum".

B. P. schrieb:

Zitaten:

Bitte... zu einem Zitat gehört eine Quellenangabe.

HTH (re)

OP #7446624
Lesenswert?

ein Beispiel vom pdf-Buch von Prof.Richard Kaiser:

In C++ kann man die zusammengehörigen Deklarationen dagegen jeweils in einen eigenen Namensbereich aufnehmen und die Elemente dann innerhalb des Namensbereichs unter ihrem angemessenen Namen ansprechen: namespace Kunden { // Ein sehr einfaches Beispiel struct Adresse { // Elemente, die für Kunden notwendig sind }; const int Max=1000; Adresse Adressen[Max]; void show(TextBox^ tb) { // ... } } // end of namespace Kunden namespace Mitarbeiter { struct Adresse { // andere Elemente wie bei Kunden }; const int Max=5000; Adresse Adressen[Max]; void show(TextBox^ tb) { } } // end of namespace Mitarbeiter

Außerhalb eines Namensbereichs kann man seine Elemente mit den Namen des Namensbereichs, dem Bereichsoperator und den Namen des Elements ansprechen:

void doSomething(TextBox^ textBox1) { Kunden::show(textBox1); Mitarbeiter::show(textBox1); }

Auf diese Weise lassen sich auch Namenskonflikte zwischen verschiedenen Bibliotheken (z.B. von verschiedenen Herstellern) vermeiden

Die C++-Standardbibliothek fasst alle ihre Elemente in den Namensbereich std. Wenn man diesen Namen nicht für einen eigenen Namensbereich verwendet (was man nie tun sollte), bekommt man mit den Namen der Standardbibliothek nie Namenskonflikte.

Angehängte Dateien:
#7446907
Lesenswert?

Harald K. schrieb:

B. P. schrieb:

void show(TextBox^ tb)

Das ist kein C++, sondern das .Net-Geraffel.

Deswegen ja mein Einwurf, dann könne man gleich C# nehmen, das macht dann viele Dinge einfacher. Das managed c++ muss man halt wollen und nimmt man eigentlich nur, wenn man unbedingt muss. Z.B. um eine .net DLL in ein bestehendes C++ Programm einzubinden. Ohne Not würde ich das nicht nehmen, dann lieber alles C++ oder alles C#. Wenn es nur um das GUI geht, wurden ja schon Alternativen genannt.

#7447132
Lesenswert?

Na ja, so schlimm ist das C++/CLI doch nicht. Es setzt aber halt ein zweites Objektmodell dem nativen C++-Objektmodell obendrüber - nämlich das der .net-Objekte. Vorteile davon sind unter anderem halt ähnliche wie anno dazumals für Objective-C++: man kann bestehenden C++-Code nehmen, einen .net-Wrapper verpassen und ziemlich schnell integrieren. In C# müssteste für jeden nativen C/C++-Funktionsaufruf sonst entweder mit COM-INterfaces arbeiten oder eben P/Invoke-Wrapper basteln - die bisweilen auch recht lustige und abgefahrene Funktionssignaturen im Resultat haben können, pinvoke.net hin oder her.

Im Endeffekt ist einfach ne 2. Abstraktion für Objekte dazugekommen:

  • new (C++) vs. gcnew (.net)
  • Pointer (* in C++) vs. Handles (^ in .net)
  • Referenzen (& in C++) vs. Ref-Handles (% in .net)

Dazu gibts 4 zusätzliche Typen von struct/class-Kombinationen:

  • ref class (.net Klasse, default private, GC)
  • ref struct (.net Klasse, default alles public, GC)
  • value class (.net Datatype struct, default private)
  • value struct (.net Datatype struct, default public)

Die structs in .net sind halt die Teile, die nur bei Konstruktion initialisiert werden können.

Als Adapter-Layer für die wechselseitige Interaktion zwischen C++ und .net ist das C++/CLI gar nicht soo schlecht. Man muss dann aber auch mit ähnlichen Dingen rechnen, wie eben bei .net auch: Assemblyreferenzen setzen, GlobalassemblyCache, Nugets usw. usf.

OP #7447228
Lesenswert?

Falk S. schrieb:

Na ja, so schlimm ist das C++/CLI doch nicht.

Ich bin für C.Auch emotional! C gibst Programmierung Freiheit. Das Visual Studio muss ich besser kennenlernen. Bei neuen Sachen um Zeit zu sparen bewegt sich oft man wie ich das nenne: intuitiv. Das läuft ohne Fehler: Sender: VarBesch(&i, z, 0, VarInfo, "[MinimumPixelE→Text]", MinimumPixelE->Text, EDITEXT);

VarBesch(&i, z, 0, VarInfo, "[MinimumPixelE→Text]", this->MinimumPixelE, EDITEXT);

Empänger Funktionen: short VarBesch (unsigned short* i, unsigned short z, char MemReservieren, VarInfo_typ* VarInfo, char* Name, System::String^ a, char Art) { ... VarInfo[i].TextString = &a; ... } short VarBesch(unsigned short i, unsigned short z, char MemReservieren, VarInfo_typ* VarInfo, char* Name, System::Windows::Forms::TextBox^ a, char Art) { ... VarInfo[*i].TextBoxPointer = &a; ... }

Angehängte Dateien:
OP #7447281
Lesenswert?

Bingo!: short VarBesch(unsigned short* i, unsigned short z, char MemReservieren, VarInfo_typ* VarInfo, char* Name, System::Windows::Forms::TextBox^ a, char Art) { ... VarInfo[*i].TextBoxPointer = &a; (*VarInfo[*i].TextBoxPointer)->Text = " DD"; ... } Ich kann die einzelnen Elemente von Forms auch beschreiben. Info über Pointer in VarInfo[]; Das ist das, was ich wollte!. Bei Borland Builder könte ich alle Pointer durch cast in DWORD umwandeln. Jetzt brauche ich für jede Art von Pointer eine Funktion. Aber das ist kein Problem!

Beitrag #7447282 wurde von einem Moderator gelöscht.
#7447304
Lesenswert?

B. P. schrieb:

Ich bin für C.Auch emotional! C gibst Programmierung Freiheit.

Aha.

B. P. schrieb:

short VarBesch (unsigned short* i, unsigned short z, char MemReservieren, VarInfo_typ* VarInfo, char* Name, System::String^ a, char Art)

Das ist kein C und auch kein C++, sondern die von MS verwürgste Pseudo-C++-Variante "Managed C++" bzw. "C++/CLI".

Von Freiheit ist das mit Abstand am weitestens entfernt.

#7447339
Lesenswert?

Harald K. schrieb:

Das ist kein C und auch kein C++, sondern die von MS verwürgste Pseudo-C++-Variante "Managed C++" bzw. "C++/CLI".

Von Freiheit ist das mit Abstand am weitestens entfernt.

Nein, das ist Quatsch.

Das war so ziemlich das Beste, was man machen könnte, um diesen unsäglichen Schrotthaufen C++ den Zugang zu in einer prinzipiell viel sichereren Sprache verfaßten GUI zu ermöglichen.

Das Konzept ist logisch: GUI = extrem hohe Komplexität = viel Möglichkeiten für Programmierfehler. Sehr viele davon können allein durch das sehr viel sicherere Sprachkonzept der der .net-Sprachen gefangen werden. Dazu kommt dann noch, dass GUI-Entwicklung in einer .net-Umgebung sehr viel komfortabler ist.

Ich persönlich nutze den Kram allerdings auch nicht. Nur (zähneknirschend) den umgekehrten Weg. Also ausgehend von .net und dann Import und Wrapping dieser immanent unsicheren C/C++ -Scheiße. Allein am Aufwand, der dazu oft nötig ist, kann man mühelos erkennen, wie schlecht dieser Code oft ist, allein schon vom Design her...

#7448819
Lesenswert?

Harald K. schrieb:

Wenn man zum Programmieren zu doof ist, dann muss man sich so ausdrücken. Ist man das nicht, braucht man auch keine Programmiersprache, die einem beim Aufs-Töpfchen-Gehen an die Hand nimmt und nett Bubu zu einem sagt.

Tja, so sagte der C/C++ -"Programmierer" und verfasste die nächste Sicherheitslücke...

Man braucht sich doch nur die Statistik der Sicherheitslücken anschauen, um es ganz klar zu erkennen: die übergroße Mehrheit davon resultiert ALLEIN aus dem schwachen bis nicht vorhandenen Schutz vor Programmierfehlern in C/C++.

Dabei muß man dann noch etwas differenzieren. C++ hätte theoretisch zumindest das Potential gehabt, deutlich sicherer zu werden als C. Hat bloß nicht geklappt, weil eben auch C-Code "adaptiert" werden sollte. Und damit alle Schweinereien, die C ungestraft erlaubt. Das war das vorgeburtliche AUS für die Sicherheit von C++.

Dazu kommt dann noch die "UB"-Thematik. Wenn etwas "UB" ist, dann hat der Compiler zuverlässig zu warnen. Alles andere ist systematisch falsch. Menschen machen nunmal Fehler. Ein Compiler/eine Sprache muss diesem Sachverhalt Rechnung tragen, sonst taugt er/sie nix.

Zumal bei einer Sprache wie C++, deren Dokumentation sich über TAUSENDE Seiten erstreckt.

#7448826
Lesenswert?

C-hater schrieb:

Dabei muß man dann noch etwas differenzieren. C++ hätte theoretisch zumindest das Potential gehabt, deutlich sicherer zu werden als C. Hat bloß nicht geklappt, weil eben auch C-Code "adaptiert" werden sollte. Und damit alle Schweinereien, die C ungestraft erlaubt. Das war das vorgeburtliche AUS für die Sicherheit von C++.

Hätte nicht, denn C++ war von Anfang als Erweiterung von C gedacht. Also hat man den ganzen Kram aus C adaptiert. Von C++ kommt man auch nicht mehr weg. In 40 Jahren C++ ist eine Abhängigkeit entstanden. Haufenweise Code Bases sind in C++ geschrieben, rewrites - zu teuer. Stattdessen wird C++ regelmäßig geupdated auf Verlangen der Industrie. Der Nachwuchs ist auch nicht wirklich da für C++ - zu kompliziert, zu umfangreich. Lieber Python lernen und den nächsten KI-Wichs zusammenkopieren.

#7448832
Lesenswert?

C-hater schrieb:

Tja, so sagte der C/C++ -"Programmierer" und verfasste die nächste Sicherheitslücke...

Wären C und C++ so schlecht, wie Du glaubst, dann gäbe es deutlich mehr Sicherheitslücken. Die Sprachen legen nur halt etwas höhere Messlatten an die Programmierer, wenn man da billige Deppen frisch von der Uni ranlässt, dann kann dabei halt Murks rauskommen.

Millionen von stabil und zuverlässig laufenden in C und C++ entwickelten Anwendungen beweisen, daß es anders geht als Du Dir zusammenreimst.

Wieso liest man entsprechendes Gekreische von Dir eigentlich nicht über Assembler?

#7448844
Lesenswert?

Harald K. schrieb:

Wären C und C++ so schlecht, wie Du glaubst, dann gäbe es deutlich mehr Sicherheitslücken.

Unsinn. Die existierenden und immer wieder neu aufpoppenden reichen wohl völlig aus, um meine Einschätzung eindrucksvoll zu beweisen.

Zumal: bei genauerer Betrachtung würden sich wohl auch noch mehr finden lassen...

Millionen von stabil und zuverlässig laufenden in C und C++ entwickelten Anwendungen beweisen, daß es anders geht als Du Dir zusammenreimst.

Welche? Nenne auch nur eine einzige!

Wieso liest man entsprechendes Gekreische von Dir eigentlich nicht über Assembler?

Weil reiner Assembler natürlich konzeptionell vollkommen unsicher ist. Aber: da C Assembler-Teile als integralen Bestandteil enthält, erbt es natürlich auch noch deren Unsicherheit! Und C++ wiederum erbt das von C.

Sprich: Wenn man was Sicheres will, sind Assembler, C und C++ tabu.

Wenn man hingegen was Schnelles und Effizientes will (und Sicherheitsaspekte eher eine untergeordnete Rolle spielen oder Probleme durch die Umgebung (OS) gefangen werden können), ist Assembler das Mittel der Wahl.

Was man eigentlich niemals wirklich braucht, ist: C und C++. Es sei denn, man will (meistens eher: muss) vorhandenen Code verwenden, der in diesen Sprachen geschrieben wurde. Leider ist das aber der Normalfall. Mit den bekannten Folgen bezüglich der Sicherheit...

OP #7449722
Lesenswert?

Ich experimentiere weiter... Ich wollte gerne in der Visual Studio klassische Elemente von C++ verwenden. Das brachte eine Menge von Fehlermeldungen: #include <string.h> #include <stdlib.h> #include <iostream> namespace std; ... { string s=“ggg“; ….

Aber das funktioniert!:

#include <string.h> #include <stdlib.h> #include <iostream> //namespace std; ... { std::string s=“ggg“; ….

Also ohne: „namespace std;“ aber explizit (Beispiel…) mit dem präfix std:: geht das!

#7449770
Lesenswert?

B. P. schrieb:

Das brachte eine Menge von Fehlermeldungen:

Der Compiler sagt aber nicht einfach nur "Fehler", sondern sagt dir, was der Fehler ist. Lies die Meldungen und versuche, sie zu verstehen.

B. P. schrieb:

Also ohne: „namespace std;“ aber explizit (Beispiel…) mit dem präfix std:: geht das!

Weil "namespace std;" keinen Sinn ergibt. Du meinstest vermutlich "using namespace std;", was man allerdings in der Regel vermeiden sollte, weil es dem Sinn von Namespaces widerspricht. Das wurde hier eigentlich alles schon erklärt.

OP #7449860
Lesenswert?

Rolf M. schrieb:

Der Compiler sagt aber nicht einfach nur "Fehler", sondern sagt dir, was der Fehler

Es sind so viele Fehler! Und das sind auch verschiedene Fehler, die auf die #include Standard Files zeigen. Das muss man verstehen, dass Visual Studio nicht alles von C++ akzeptiert. Nur manche Sachen die man mit dem Präfix std:: gekennzeichnet.

#7449901
Lesenswert?

B. P. schrieb:

short VarBesch (unsigned short* i, unsigned short z, char MemReservieren, VarInfo_typ* VarInfo, char* Name, System::String^ a, char Art)

Die Deklaration von VarInfo_typ wär durchaus hilfreich gewesen, auch wenns jetzt funktionieren mag.

Harald K. schrieb:

Das ist kein C und auch kein C++, sondern die von MS verwürgste Pseudo-C++-Variante "Managed C++" bzw. "C++/CLI".

Von Freiheit ist das mit Abstand am weitestens entfernt.

Na ja, ECMA-Standard isses ja, gibt halt nur wenig freie Compiler, die es können - müsste ja wohl quasi irgendwie über Mono laufen, wenn es denn gehen sollte.

Prinzipiell geb ich dir recht: GUI-Elemente würde ich auch nicht unbedingt in C++/CLI bauen wollen, da gibts bessere .net-Sprachen für sowas.

Zum Zweck der Adaptierung von nativem Code in managed Code, isses ok und sogar teilweise eine gute Alternative, weil du eben auch C/C++-spezifische Sachen wie Bitfields oder templates damit ansteuern kannst. Solche gemangelten Sachen über p/invoke zu machen fände ich noch viel schlimmer. Man kann zwar sagen: gut, dann design halt ne gescheite C-Export-API für die DLL, die solchen Mist gar nicht erst zulässt. Dafür sieht dann dein p/invoke-Wrapper auf C#-Seite auch wieder schrottig aus und ist meistens so ein häßliches Konstrukt aus einer statischen Klasse - die aber trotzdem dann über das von C aus exportierte void*-Handle irgendwo noch Zustand hat. D.h. man frickelt dann 2 Wrapper-Layer in C# um eine popelige DLL objektorientiert anzusteuern... (eigentliche Klasse + statische p/invoke-Klasse drunter)

Grosser Nachteil sehe ich allerdings für C++/CLI auch, sobald sowas wie CMake ins Spiel kommt - mit dem MSVC hat man zwar wohl theoretisch ne Chance und .net Nugets/Referenzen gehen wohl auch mittlerweile, aber so richtig geil ist das glaub ich noch nicht.

#7449909
Lesenswert?

B. P. schrieb:

Rolf M. schrieb:

Der Compiler sagt aber nicht einfach nur "Fehler", sondern sagt dir, was der Fehler

Es sind so viele Fehler!

Es ist sinnvoll, mit dem ersten anzufangen, denn die anderen sind meist Folgefehler, weil der Compiler den Faden verloren hat, oder sie dienen der Erläuterung des vorherigen Fehlers, indem sie z.B. auf eine für den Fehler relevante Stelle im Code verweisen.

Und das sind auch verschiedene Fehler, die auf die #include Standard Files zeigen.

Dann sind diese hoffentlich Tel der oben genannten Erläuterung, denn in den Standard-Headern sollten ja keine Fehler sein. Alternativ kann das auch durch eigene Header kommen, die vor den Standard-Headern eingebunden wurden.

Das muss man verstehen, dass Visual Studio nicht alles von C++ akzeptiert.

Das kommt darauf an, wie du es nutzt. Wenn es auf echtes C++ eingestellt ist, sollte es das schon auch akzeptieren. Hast du ein Beispiel, was da nicht geht?

#7449971
Lesenswert?

B. P. schrieb:

Es sind so viele Fehler! Und das sind auch verschiedene Fehler, die auf die #include Standard Files zeigen. Das muss man verstehen, dass Visual Studio nicht alles von C++ akzeptiert. Nur manche Sachen die man mit dem Präfix std:: gekennzeichnet.

Na ja, die <string.h> sollte eigentlich ja auch was anderes als die <string>-Header sein. Im Normalfall sollte nach üblicher C++-Notation die <string.h> die <cstring> sein (die solches Zeug wie strcmp, strcat, strtok, usw. hält), während <string> und <wstring> halt eben die C++-STL-strings in beiden Geschmacksrichtungen verfügbar macht. Lustig wirds bei deinem obigen C++/CLI-Beispiel, wo System::String und glaube System::SecureString noch dazukommen kann.

U.a. mal noch folgender Tipp: sowas lieber nicht machen:

1
struct Foo
2
{
3
  System::String ^ m_text;
4

5
  Foo() : m_text(gcnew System::String()) { }
6
  
7
  std::string toString() { /*return m_text.<irgendeine fkt, falls es überhaupt geht>* /};
8

9
};

Imho besser wäre: managed Code drumrum, als Member dann aber nur native Typen:

1
public ref struct Foo
2
{
3
  Foo() : m_text() {}
4
  ~Foo() { this->!Foo(); } // hier drunter nur natives c++ zeug aufräumen
5
  !Foo() {} // hier drin nur .net zeug aufräumen, wird vom GC aufgerufen
6

7
  std::string m_text;
8

9
  System::String^ toString() { return gcnew System::String(m_text.c_str()); }
10
};

Soll heißen: nicht native Klassen .net Membervariablen halten lassen, besser umgekehrt. Begründung: man hat oft keine Möglichkeit, aus den .net Klassen native Informationen rauszuziehen. Zusätzlich noch dazu kommend: die weniger günstige Variante von oben hat potentiell den Nachteil, dass du MemoryLeaks beim Aufruf durch .net kriegen kannst, wenn das .net den !Foo-Finalizer aufruft und der nicht gescheit implementiert ist. Das Problem mit diesem Disposing haste aber in C# wohl auch, C++/CLI hin oder her - ist scheinbar ne übliche .net-Interop-Problematik.

OP #7453913
Lesenswert?

Wie kann ich Pointer auf Pointer i Visual Studio definieren:

1
//Pointer auf System::String 
2
//Das funktioniert ohne Fehler
3
System::String^ SysStringPoint;
4
SysStringPoint = MinimumPixelAbstandE->Text;
5
    
6
//Pointer auf Pointer von System::String 
7
System::String^ *SysStringPPoint;  
8
SysStringPPoint = MinimumPixelAbstandE->Text;
9
//Diese Zuweisung wird
10
//nicht akzeptiert.Was muss ich hier ändern 
11
//(Zusatz für Wandlung...)

weiss das jemand...?

#7454036
Lesenswert?

B. P. schrieb:

1
> //Pointer auf Pointer von System::String
2
> System::String^ *SysStringPPoint;
3
> SysStringPPoint = MinimumPixelAbstandE->Text;
4
> //Diese Zuweisung wird
5
> //nicht akzeptiert.Was muss ich hier ändern
6
> //(Zusatz für Wandlung...)
7
>

Um sowas wie ein Pointer zu bekommen muesstest du das pinnen, macht aber auf ein Objekt wie System::String kein Sinn. Wozu meinst du soll ein Zeiger gut sein? Was fuer ein Problem hast du mit der .NET referenz?

Hat es einen speziellen Grund warum Du C++/CLI verwendest? Falls du keinen alten C++ code in der WinForms GUI verwenden musst -> kipp das Ganze und wechsle auf C#.

#7454120
Lesenswert?

Falk S. schrieb:

Na ja, die <string.h> sollte eigentlich ja auch was anderes als die <string>-Header sein. Im Normalfall sollte nach üblicher C++-Notation die <string.h> die <cstring> sein

Jein. string.h wäre nicht im namespace std gekapselt und bei cstring schon. Man sollte cstring bevorzugen sonst gibts schnell böse Überraschungen. Aber warum kein normaler C++ String? string.h ist die C Bibliothek für strings :D

#7454199
Lesenswert?

B. P. schrieb:

Wie kann ich Pointer auf Pointer i Visual Studio definieren:

1
> //Pointer auf System::String
2
> //Das funktioniert ohne Fehler
3
> System::String^ SysStringPoint;
4
> SysStringPoint = MinimumPixelAbstandE->Text;
5
>

Ja, SysStringPoint ist initial ein nicht initialisiertes Handle. Quasi eine null-Zuweisung in .net. Durch die Zuweisung drunter übergibst du dem Handle SysStringPoint das Handle von MinimumPixelAbstandE->Text, was seinerseits ein .net-Objekt zu sein scheint (wenn dieser Code bei dir so kompiliert - ich kenn ja den Datentyp von MinimuPixelAbstandE nicht, nehme aber an, das ist eine Art Textfeld).

1
> //Pointer auf Pointer von System::String
2
> System::String^ *SysStringPPoint;
3
> SysStringPPoint = MinimumPixelAbstandE->Text;
4
> //Diese Zuweisung wird
5
> //nicht akzeptiert.Was muss ich hier ändern
6
> //(Zusatz für Wandlung...)
7
>

Das wird nicht funktionieren - niemals ein .net-Handle und einen nativen Pointer gleichsetzen. Du kannst keinen Pointer (*) auf ein vom .net-Framework verwaltetes Objekt bekommen, weil Pointer unter der Kontrolle der C/C++-Runtime sind (z.B. MSVCRT), .net-Gedönse aber halt eben im CommonLanguageInterface drinne sind. D.h. soviel wie: es wird aus dem .net-Objekt eine Intermediate-Language (IL) wie bei Java erzeugt und von einem .net-'VirtualMachine' ausgeführt, welche die IL eben selber in nativen Code übersetzt. Und weil du da auch nicht auch nicht drumrum kommst, dafür gibst ja C++/CLI - damit du beide Welten verbinden kannst.

Was du ja eigentlich bezwecken scheinst, ist es ja, den Text aus dem Steuerelement auszulesen. Und dafür musste halt mit Mitteln von C++/CLI aus dem .net-Objekt System::String das raus-'marshallen'. Beim Visuellen Studio sollte es wohl so ähnlich ausschauen:

1
#include <msclr/marshal_cppstd.h>
2

3
System::String^ text(MinimumPixelAbstandE->Text); 
4
std::string nativeText=msclr::interop::marshal_as< std::string >(text);

Und dann kannste den 'nativeText' mit normalen C++-Mitteln weiter verarbeiten. Der umgekehrte Weg sollte einfach über Konstruktor möglich sein:

1
MinimumPixelAbstandE->Text = gcnew System::String("Mein Toller Text");
OP #7454302
Lesenswert?

Ich teste Programmierung in C++/CLI mit Forms. Jetzt mache ich einen Versuch mein altes Programm, das ich unter C++ Builder geschrieben habe, in Visual Studio mit Forms zu umschreiben. Am Anfang alle Steuerelemente von Form (CheckBox ComboBox, TextBox…) sind in einem Array von diesem Typ angemeldet:

1
typedef struct {
2
    char* Name;
3
        DWORD  Pointer;
4
        System::Windows::Forms::TextBox^  *TextBoxPointer;
5
        System::Windows::Forms::CheckBox^ *CheckBoxPointer;
6
        System::Windows::Forms::ComboBox^* ComboBoxPointer;
7
        char Art;
8
} VarInfo_typ;

Abspeicherung von dem Inhalt von diesen Steuerelementen in einem File läuft das in einer for-Schleife. Genauso ist es beim Laden aus einem File.Im C++ Builder brauchte ich sogar nur DWORD Pointer, weil ich könnte alle Pointertypen von Steuerelementen in DWORD durch cast umwandeln. Ich kenne mich absolut nicht mit .NET. Also ^ ist kein Pointer sondern nur Handle? Könnte man dieses Handle (das ist auch nur eine Zahl...? ) nicht abspeichern z.B. in meiner VarIno_typ Structur? Und das später abzurufen? Vielleicht geht das auch anders...

#7454306
Lesenswert?

B. P. schrieb:

Genauso ist es beim Laden aus einem File.Im C++ Builder brauchte ich sogar nur DWORD Pointer, weil ich könnte alle Pointertypen von Steuerelementen in DWORD durch cast umwandeln.

Warum sollte man das machen? Das ist grauenvoll. Es hat schon seinen Grund, warum es Pointertypen gibt. Abgesehen davon reicht - wie schon gesagt wurde - auf einer 64-Bit-Plattform ein DWORD nicht aus, um einen Zeiger zu speichern.

Ich kenne mich absolut nicht mit .NET. Also ^ ist kein Pointer sondern nur Handle? Könnte man dieses Handle (das ist auch nur eine Zahl...? ) nicht abspeichern z.B. in meiner VarIno_typ Structur? Und das später abzurufen? Vielleicht geht das auch anders...

Warum willst du zwanghaft das Typsystem umgehen?

#7454524
Lesenswert?

B. P. schrieb:

Am Anfang alle Steuerelemente von Form (CheckBox ComboBox, TextBox…) sind in einem Array von diesem Typ angemeldet:

Wenn du das willst, dann musst du das in ein ref class/struct packen und in einen CLR/.NET container ablegen. Aber wozu soll man das wollen?

Ich kenne mich absolut nicht mit .NET. Also ^ ist kein Pointer sondern nur Handle? Könnte man dieses Handle (das ist auch nur eine Zahl...? ) nicht abspeichern z.B. in meiner VarIno_typ Structur? Und das später abzurufen? Vielleicht geht das auch anders...

Die moeglichkeit besteht mit GCHandle.Alloc() und GCHandle.ToIntPtr(). Das gibt dir ein Zeiger (ein IntPtr, kein DWORD, IntPtr.ToPointer gibt dir ein native Zeiger) auf ein beliebiges .NET Objekt. mittels GCHandle.FromIntPtr() kommst du vom Zeiger wieder zum .NET Objekt. Ob du damit Gluecklich wirst?

Mein Tip an dich:

  • Willst du bei C++ bleiben dann schau dir QT an.
  • Gefaellt dir WinForms, wechsle zu C#
  • Scheust du dich nicht von einer steilen Lernkurve, dann C# mit WPF oder Avalonia

Was du dir auf jeden Fall anschauen solltes ist 'data binding'. Das koenenen alle genannten GUI frameworks. Ueber Steuerelemente zu iterieren und befuellen macht man eigentlich nicht mehr.

#7454560
Lesenswert?

B. P. schrieb:

Ich teste Programmierung in C++/CLI mit Forms. Jetzt mache ich einen Versuch mein altes Programm, das ich unter C++ Builder geschrieben habe,

Du wechselst also die Programmiersprache, dessen musst Du Dir sehr deutlich bewusst sein.

Der "C++ Builder" verwendete echtes C++, "C++/CLI" ist kein C++, sondern eine Microsoft-Speziallösung, um optisch ähnliches wie C++ auf den .Net-Unterbau draufzuflanschen.

OP #7456983
Lesenswert?

Meine Übersetzung von Borland Builder in Microsoft Visual Studio von Microsoft macht Fortschritte. Mein altes Borlands Projekt ist hier: Beitrag "PCB von BMP drucken" MyForm mit allen Steuerelementen ist schon fertig. Als erste habe ich die Abspeicherung und Laden von Daten (Parameter) von Steuerelementen (TextBox, ComboBox, CheckBox) in Angriff genommen. Nach dem Start vom Programm werden alle Steuerelemente mit den Initialisierungswerten vorbesetzt:

1
  Breite->Text = "3.05";
2
   ...
3
   RundungsModusVertikal->SelectedIndex = 2;
4
   ...
5
   Dreh90Grad->Checked = 1;

Danach werden Informationen von allen Steuerelementen über eine Funktion in VarInfo_typ* VarInfo eingespeichert:

1
VarBesch(&i, z, Mem, VarInfo, "[MinimumPixelE->Text]", TEXT_String);
2
 
3
 VarBesch(&i, z, Mem, VarInfo, "[Spiegeln->Checked]", CHECK_Bool);
4
 
5
VarBesch(&i, z, Mem, VarInfo, "[MM_INCH->SelectedIndex]", COMBO_Zahl);
1
System:String^* pp;
2
    pp = &(thisMinimumPixelAbstandEText);
3
bringt Fehler: Fehler C2102 "&" erwartet L-Wert

Bei Borland Builder könnte ich auch Pointer von allen Steuerelementen in VarInfo übertragen. Leider unter Visual Studio ist das nicht möglich.

Angehängte Dateien:
#7457005
Lesenswert?

B. P. schrieb:

1
System:String^* pp;
2
>     pp = &(thisMinimumPixelAbstandEText);
3
> bringt Fehler: Fehler C2102 "&" erwartet L-Wert

Na ja, lass doch einfach den * und den & weg und schon geht's.

Warum? Aktuell deklarierst du die Variable als vom Datentyp Pointer-auf-.net-Handle. Wie oben schon mal geschrieben ist das halt nicht so sonderlich große Idee, weil eben .net das eine (verwaltet übers .net-Framework) und C++-Code halt ne andere Sportart ist (nativ übersetzt).

Wenn du unbedingt deine Steuerelemente in ne Klasse packen willst, dann kannste deinen VarInfo_Typ halt so ändern. Btw. typedefs für structs ist in C++ eigentlich nicht unbedingt nötig, du kannst den Namen auch hinter's 'struct' schreiben (und das typedef weglassen).

Soll heißen, aus:

1
typedef struct {
2
    char* Name;
3
    DWORD  Pointer;
4
    System::Windows::Forms::TextBox^  *TextBoxPointer;
5
    System::Windows::Forms::CheckBox^ *CheckBoxPointer;
6
    System::Windows::Forms::ComboBox^* ComboBoxPointer;
7
    char Art;
8
} VarInfo_typ;

könnte werden:

1
struct Steuerelemente /* immer noch ungeeigneter Name, aber besser als oben das */
2
{
3
    char* Name = nullptr;
4
    DWORD  Pointer = 0;
5
    System::Windows::Forms::TextBox^  TextBoxPointer;
6
    System::Windows::Forms::CheckBox^ CheckBoxPointer;
7
    System::Windows::Forms::ComboBox^ ComboBoxPointer;
8
    char Art='\0';
9
};

Ist nicht unbedingt direkt jetzt 'schön' weil's ne normale struct ist und keine .net-ref-struct, sollte aber gehen. Die Initialisierung deiner Steuerelemente (TextBoxPointer, usw.) machste wie oben geschrieben ohne & und *.

OP #7457010
Lesenswert?

Falk S. schrieb:

Die Initialisierung deiner Steuerelemente (TextBoxPointer, usw.) machste wie oben geschrieben ohne & und *.

Ich habe beide Versionen getestet. Leider ohne Erfolg.

1
 So würde ich einen Pointer (auf Pointer) auf TextBox
2
  bekommen und dann könnte ich TextBox Elemente lesen
3
  und beschreiben. So funkt das bei Borland Builder aber nicht
4
  beim Visual Studio:
5
 System::Windows::Forms::TextBox^  *TextBoxPointer;
6
So bekomme ich nur eine Kopie vom Inhalt vom TextBox:
7
System::Windows::Forms::TextBox^  TextBoxPointer;
#7457016
Lesenswert?

B. P. schrieb:

Meine Übersetzung von Borland Builder in Microsoft Visual Studio von Microsoft macht Fortschritte. Mein altes Borlands Projekt ist hier: Beitrag "PCB von BMP drucken" MyForm mit allen Steuerelementen ist schon fertig.

Wuerde sagen: Hat sich gelohnt, 50% Groessenersparnis mit der neuen Technologie...

Wobei, mich wundert jetzt auch nicht mehr die Wahl von WinForms mit C++/CLI.

Bist Du zufrieden damit? Moechtest Du ein beispiel wie man das moderner gestalten koennte?

OP #7457100
Lesenswert?

Um das fehlendes Zugriff über Pointer auf Steuerelemente umzugehen, habe ich eine Funktion mit switch/case mit der ich Korrespondiere von meinen for schleifen zum save und load von Daten. Manche Teile von Meinem Programm unter Borland Builder sind fast gleich und nach kleiner Anpassung direkt zum Übernehmen. Beispiel:

1
private: System::Void LoadParameter_Click(System::Object^ sender, System::EventArgs^ e)
2
{  HANDLE InDateiHandle;
3
  DWORD  FileInLaengeH, FileInLaenge;
4
  HANDLE StandardHeapHandle;
5
  LPVOID PointerAllocPuff;
6
  String^ MyFileNameString;
7

8
  Meldung2->Text = "";
9
  InDateiHandle = INVALID_HANDLE_VALUE;
10
  PointerAllocPuff = 0;
11
   …………. 
12
MyFileNameString = openFileDialog1->FileName;
13
pin_ptr<const wchar_t > wch = PtrToStringChars(MyFileNameString);
14
InDateiHandle = CreateFile(wch,GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);

Ich lese das ganze File in Puffer und arbeite nur mit Bytes. Alle Variablen werden mit ASCII Namen (in Klammer []) abgespeichert. Beim lesen von File wird nur nach diesen Namen gesucht.Mit diesem Wirrwarr von tausenden Klassen will ich nichts zu tun haben. Die Sache ist zu weit gelaufen. Niemand wagt sich sagen dass der König nackt ist.

#7459507
Lesenswert?

Hallo B.P., darf ich Dich noch mal fragen wieso Du Dir diese Mühe machst ? Es gibt die C++ Builder Community edition for free. Du darfst halt kein Geld damit verdienen bzw Max 5000$. Hast Du schon mal probiert Deine Projekte/Projekt dort mit der neuen IDE zu laden?

Ist nur ein Gedanke …

Gruß Sven

OP #7470178
Lesenswert?

C++/CLI erlaubt Zeiger auf eine C++/CLI-Verweisklasse oder -Schnittstellenklasse! Man muss das so definieren wie im Beispiel:

1
SauberMinTVor->Text = "321";
2
cli::interior_ptr <System::Windows::Forms::TextBox^> kkk;
3
kkk =  & (System::Windows::Forms::TextBox^)SauberMinTVor;
4
System::Windows::Forms::TextBox^ pp = *kkk;
5
pp->Text = "123";
OP #7471966
Lesenswert?

Das ist so unter C++ Builder von Borland gegangen. Mit Visual Studio geht das so nicht.Im Visual Studio bekommt man (intuitives Denken…)nur eine Kopie von Pointer auf einen Formsfeld auf dem Stack.Zwischenspeicherung vom Pointer auf Formsfelder in einer Funktion bringt nichts, weil nach dem Verlassen der Funktion Stack ändert sich.

1
for(i=0;....){
2
VarBesch(B,&i, z, Mem, VarInfo, "[Form1->MinimumPixelE->Text]", *(&SauberMinT), TEXTBOX);
3
VarBesch(B,&i, z, Mem, VarInfo, "[Form1->Spiegeln->Checked]",
4
*(&Spiegeln), CHECKBOX);
5
VarBesch(B,&i, z, Mem, VarInfo, "[Form1->MM_INCH->ItemIndex]",
6
 *(&MM_INCH), COMBOBOX);}

Im „for“ Block werden alle Formsfelder angemeldet. Nach erstem Durchlauf wird maximale Anzahl von Felder berechnet (==i) und wird Speicher allokiert für VarInfo. Danach werden alle notwendige Daten in der Funktion VarBesch()in die VarInfo geschrieben. Jedes Feld bekam jetzt auch eine Nummer(ErsatzPointer...) In der Funktion VarBesch() sind die Pointer auf Formsfelder aktuell und ich Kommuniziere mit diesem „for“ mit Befehlen zum Lesen oder Beschreiben von Formsfelder. Bei einem neuen Formsfeld zum Save oder Load wird nur eine neue Meldungszeile eingeführt.Das alles wäre auch mit „case“ möglich, aber mit viel Handarbeit.

#7471991
Lesenswert?

B. P. schrieb:

Das ist so unter C++ Builder von Borland gegangen. Mit Visual Studio geht das so nicht.

Doch, das geht selbstverständlich auch "mit Visual Studio" so. Auf genau diese Art und Weise.

Wenn man es schafft, den C++-Compiler zu verwenden, und nicht auf die Glitzer-Vorgaukel-Welt des .Net-Geraffels 'reinzufallen, das eine Pseudoprogrammiersprache mit dem irreführenden Namen "C++/CLI" anbietet.

Mit Visual Studio kann man natives C++ verwenden, und das verhält sich wie natives C++. "WinForms" aber braucht den .Net-Unterbau, und kann daher nicht mit C++ genutzt werden.

Wie oft muss man das hier eigentlich noch wiederkäuen?

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren