DLL Datei ersetzen funktioniert nicht bei Programmstart?
-
audacia schrieb:
Das geht natürlich nur, wenn du ein Stringliteral zurückgibst. Ansonsten hast du entweder ein Speicherleck (wenn du den Rückgabewert mit new oder malloc allozierst) oder eine Zugriffsverletzung (wenn du z.B. den Rückgabewert der c_str()-Methode eines lokalen Strings zurückgibst).
Es geht auch ohne Stringliterale Speicherlecks und Zugriffsverletzungen zu vermeiden. Man muss nur etwas Sorgfalt walten lassen und den Speicherbereich nicht zu zeitig freigeben sowie nach Freigabe der dll nicht mehr darauf zugreifen.
Bei deinem Beispielcode schreibst du in const char* Bereiche von String-Variablen, was so auch nicht zulässig ist. Wenn schon, dann würde ich hier new und delete bemühen.
-
Braunstein schrieb:
Es geht auch ohne Stringliterale Speicherlecks und Zugriffsverletzungen zu vermeiden. Man muss nur etwas Sorgfalt walten lassen und den Speicherbereich nicht zu zeitig freigeben sowie nach Freigabe der dll nicht mehr darauf zugreifen.
Da wäre ich sehr gespannt, wie du das sauber lösen wolltest.
Braunstein schrieb:
Bei deinem Beispielcode schreibst du in const char* Bereiche von String-Variablen, was so auch nicht zulässig ist. Wenn schon, dann würde ich hier new und delete bemühen.
Wieso sollte das nicht zulässig sein?
String::c_str() gibt keinen const-Zeiger zurück:// ustring.h, l.144 wchar_t* __fastcall c_str() const { return (Data)? Data: const_cast<wchar_t*>(L"");}
Und wenn man nicht c_str() verwenden kann (z.B. in std::string), so nehme man einfach &string[0].
-
Eine kleine Korrektur muß ich noch anbringen, und zwar in der Benutzung der Beispielfunktion:
void foo (void) { String s; s.SetLength (200); unsigned len = GetAString (s.c_str (), s.Length () + 1 /* including '\0' */); s.SetLength (len); // <-- sollte unkonditionell ausgeführt werden if (len > s.Length ()) // more than 200 characters GetAString (s.c_str (), len + 1); }
-
audacia schrieb:
Ich habe erklärt, warum es mit Zeichenkettenliteralen funktioniert, du aber für alle anderen Fälle eine Zugriffsverletzung bekommst. Ich habe erklärt, warum man keine Objekte zwischen verschiedenen Modulen herumreichen darf, ohne Vorkehrungen getroffen zu haben. Ich habe erklärt, wie man dennoch einen String zurückgibt, und ich habe dir sogar ausführlichen und lauffähigen Beispielcode gegeben. Daß du es dennoch schaffst, das alles rundweg zu ignorieren, spricht nicht eben für deine kommunikative Befähigung.
Wenn ich meine DLL Statisch einbinde, dann funktioniert das mit dem String.
Wirklich seltsam...
-
audacia schrieb:
Ich habe erklärt, warum es mit Zeichenkettenliteralen funktioniert, du aber für alle anderen Fälle eine Zugriffsverletzung bekommst. Ich habe erklärt, warum man keine Objekte zwischen verschiedenen Modulen herumreichen darf, ohne Vorkehrungen getroffen zu haben. Ich habe erklärt, wie man dennoch einen String zurückgibt, und ich habe dir sogar ausführlichen und lauffähigen Beispielcode gegeben. Daß du es dennoch schaffst, das alles rundweg zu ignorieren, spricht nicht eben für deine kommunikative Befähigung.
Wenn ich meine DLL Statisch einbinde, dann funktioniert das mit dem String.
Wirklich seltsam...Edit: Sry wegen doppel Post, der Server hing bei mir.
-
Nein, tut es nicht. Es kann sein, daß der Memory-Manager sich dann etwas anders verhält und sich zufällig für Lazy Deallocation entscheidet, so daß der Puffer noch zugreifbar, obgleich bereits freigegeben ist.
Nur weil etwas zufällig funktioniert, muß es noch längst nicht der richtige Weg sein. Was ich oben schrieb, hat weiterhin Gültigkeit. Lies und verstehe es.
-
audacia schrieb:
Braunstein schrieb:
Bei deinem Beispielcode schreibst du in const char* Bereiche von String-Variablen, was so auch nicht zulässig ist. Wenn schon, dann würde ich hier new und delete bemühen.
Wieso sollte das nicht zulässig sein?
String::c_str() gibt keinen const-Zeiger zurückAuszug aus einer der großen STL-Implementierungen:
<a href= schrieb:
Dinkumware">
basic_string::c_strconst value_type *c_str() const;
The member function returns a pointer to a non-modifiable C string constructed by adding a terminating null element (value_type()) to the controlled sequence. Calling any non-const member function for *this can invalidate the pointer.
Und auch der Standard ist imho eindeutig...
Auszug aus dem Standard Draft schrieb:
21.3.6 - basic_string string operations [lib.string.ops]
const charT* c_str() const;
-1- Returns: A pointer to the initial element of an array of length size() + 1 whose first size() elements equal the corresponding elements of the string controlled by *this and whose last element is a null character specified by charT().
-2- Requires: The program shall not alter any of the values stored in the array. Nor shall the program treat the returned value as a valid pointer value after any subsequent call to a non-const member function of the class basic_string that designates the same object as this.
const charT* data() const;
-3- Returns: If size() is nonzero, the member returns a pointer to the initial element of an array whose first size() elements equal the corresponding elements of the string controlled by *this. If size() is zero, the member returns a non-null pointer that is copyable and can have zero added to it.
-4- Requires: The program shall not alter any of the values stored in the character array. Nor shall the program treat the returned value as a valid pointer value after any subsequent call to a non- const member function of basic_string that designates the same object as this.
allocator_type get_allocator() const;
-5- Returns: a copy of the Allocator object used to construct the string.
P.S.: Auch wenn ich durchaus weiß das der C++ Builder und const so seine Probleme haben... z.B. Das eine TDateTime const & geändert werden kann u.a.
cu André
-
asc schrieb:
Auszug aus einer der großen STL-Implementierungen:
Vielleicht ist dir ja aufgefallen, daß ich nicht den Standard-String, sondern System::String benutzt habe.
Und daß es mit std::[w]string so nicht geht, ebenso auch, wie es trotzdem machbar ist, hatte ich oben schon geschrieben. Wenigstens dir hätte ich zugetraut, daß du meine Posts genau liest, bevor du Einspruch erhebst
asc schrieb:
P.S.: Auch wenn ich durchaus weiß das der C++ Builder und const so seine Probleme haben... z.B. Das eine TDateTime const & geändert werden kann u.a.
Damit hat das nichts zu tun; außerdem ist das eigentlich seit C++Builder 2007 kein Thema mehr (falls doch, bitte ich um Beispiel zwecks Bugreport).
-
audacia schrieb:
asc schrieb:
P.S.: Auch wenn ich durchaus weiß das der C++ Builder und const so seine Probleme haben... z.B. Das eine TDateTime const & geändert werden kann u.a.
Damit hat das nichts zu tun; außerdem ist das eigentlich seit C++Builder 2007 kein Thema mehr (falls doch, bitte ich um Beispiel zwecks Bugreport).
Es ist zumindestens beim C++Builder 2007 (und sofern ich mich nicht irre hatte ich es auch unter 2009 nachgetestet) noch aktuell, und ja, es existiert ein Bugreport.
Und das mit dem Stringtyp habe ich tatsächlich überlesen, auch wenn ich es weiterhin als gefährlich ansehe, und eher als Mangel der const-correctness bei den VCL-Klassen betrachte (die ja - da aus Delphi kommend - recht wenig mit const am Hut haben). Grundsätzlich würde ich Konstrukte wo ich auf Implementationsdetails der VCL zurückgreifen muss eher meiden.
cu André
-
asc schrieb:
Und das mit dem Stringtyp habe ich tatsächlich überlesen, auch wenn ich es weiterhin als gefährlich ansehe, und eher als Mangel der const-correctness bei den VCL-Klassen betrachte (die ja - da aus Delphi kommend - recht wenig mit const am Hut haben). Grundsätzlich würde ich Konstrukte wo ich auf Implementationsdetails der VCL zurückgreifen muss eher meiden.
Gefährlich wäre es nur, wenn System::String eine Implementation von std::basic_string<> wäre. Dieser Zusammenhang besteht jedoch in keiner Weise; vielmehr ist System::String nur ein Wrapper für den Delphi-String-Typen (demnach keine VCL-Klasse, sondern das Äquivalent eines Delphi-Intrinsic!).
String::c_str(), dessen Namensgebung nur eine Konzession an 1997 bereits etablierte C++-Gewohnheiten war, gab seit jeher einen nicht konstanten Zeiger zurück. Das vereinfacht die Verwendung des Strings in Zusammenhängen wie dem von mir demonstrierten, insbesondere, da der Delphi-String und mithin auch System::String aus historischen Gründen ab 1 indiziert werden (in Delphi 1 für 16-Bit-Windows waren Strings auf 255 Zeichen begrenzt, und die Länge wurde bei Index 0 gespeichert). Demnach wäre übrigens auch &myBasicString[0] äquivalent zu &myDelphiString[1] - ein weiterer, hoffentlich hinreichend deutlicher Hinweis darauf, wie gering der Verwandtschaftsgrad der beiden String-Typen ist.
-
audacia schrieb:
Demnach wäre übrigens auch &myBasicString[0] äquivalent zu &myDelphiString[1] - ein weiterer, hoffentlich hinreichend deutlicher Hinweis darauf, wie gering der Verwandtschaftsgrad der beiden String-Typen ist.
Ich bin dennoch der Meinung das man nicht gegen die Implementierungsdetails einer Bibliothek hin entwickeln sollte. Wer sagt das die Implementierung des Stringtyps (bei gleichbleibender Schnittstelle) sich nicht irgendwann ändert?
-
asc schrieb:
Ich bin dennoch der Meinung das man nicht gegen die Implementierungsdetails einer Bibliothek hin entwickeln sollte. Wer sagt das die Implementierung des Stringtyps (bei gleichbleibender Schnittstelle) sich nicht irgendwann ändert?
Du siehst den Rückgabetyp einer sichtbaren Funktion als Implementierungsdetail an?
-
audacia schrieb:
Du siehst den Rückgabetyp einer sichtbaren Funktion als Implementierungsdetail an?
Bei den VCL spezifischen Bereichen schon, da diese niemals auf const correctness hin entwickelt wurden, und du hier einen internen Buffer bekommst. Ich sehe hier die Modifikation tatsächlich als sehr Problematisch an.
-
Darüber kann man freilich streiten. Die fehlende const-correctness in bezug auf Delphi tritt aber nur bei den vom Delphi-Compiler generierten Headerdateien explizit in Erscheinung. Die EMUVCL-Klassen wie String, Currency, Set, Variant und dergleichen sind nicht automatisch generiert worden, sondern werden von manuell entwickelt und gepflegt. Besonders dann, wenn eine Memberfunktion c_str(), data() oder swap() heißt und somit eindeutig ein C++-Idiom erfüllt, kann man vermutlich davon ausgehen, daß diese Schnittstelle bewußt definiert wurde. Daß c_str() keinen const-Zeiger zurückgibt, ist mit großer Sicherheit kein Versehen.
Über den Grund dafür kann ich nur spekulieren, aber ich halte es für nicht unwahrscheinlich, daß die semantische Parallele zu WideString::c_bstr(), das einen nicht konstanten WideString-Zeiger zurückgibt und auf ein COM-BSTR-Objekt verweist, gewahrt werden sollte. Dabei stellt sich natürlich die Frage, ob die COM-Unterstützung und der WideString-Typ (in Delphi erst ab Version 3) in C++Builder seit Anbeginn - die erste Version fiel zeitlich und technisch zwischen Delphi 2 und 3 - vorhanden war, denn ansonsten wäre dieser Kausalzusammenhang natürlich fragwürdig
Edit: Ich muß obige Behauptung revidieren. Auf dem Heimweg fiel mir ein, daß du in gewisser Weise doch recht hast - nämlich sind die String-Typen von Delphi (ausgenommen WideString, das einfach einen BSTR wrappt) über copy-on-write implementiert, und die c_str()-Methode gibt, wie du sagst, einen Zeiger auf den internen Puffer zurück. Anders als der operator [] aber ruft c_str () nicht Unique() auf, somit ist es möglich, über den darunterliegenden Puffer unbemerkt auch andere Strings zu verändern, die noch auf denselben Puffer verweisen. (Allerdings halte ich das für einen Bug in c_str().)
In meiner Anwendung tritt das zwar nicht ein, da ich mindestens mit dem Aufruf von SetLength() garantiert einen nur einfach referenzierten String habe, aber sicherheitshalber und zur Förderung der Konsistenz sollte man es dennoch anders handhaben:
void foo (void) { String s; s.SetLength (200); unsigned len = GetAString (&s[1], s.Length () + 1 /* including '\0' */); s.SetLength (len); if (len > s.Length ()) // more than 200 characters GetAString (s.c_str (), len + 1); }
(Wann wird das Forum eigentlich von diesem bedauernswürdigen Server mit einem bestimmt zweiminütigen Ping auf eine etwas aktuellere Maschine migriert?)