Beim TMemoryStream EAccessViolation -> Zugriffsverletzung


  • Gesperrt

    @Rave1703 sagte in Beim TMemoryStream EAccessViolation -> Zugriffsverletzung:

    TMemoryStream *pss = new TMemoryStream();

    Rufst du auf dem raw pointer eigentlich auch anschließend delete auf?

    ... Und der Smart-Pointer sollte eigentlich so sein:

    std::unique_ptr<TMemoryStream> pss(new TMemoryStream());
    

    Aber zum Rätselraten ist mir meine Zeit eigentlich zu schade. 😛



  • @noLust sagte in Beim TMemoryStream EAccessViolation -> Zugriffsverletzung:

    Und der Smart-Pointer sollte eigentlich so sein:
    std::unique_ptr<TMemoryStream> pss(new TMemoryStream());

    Wenn schon, dann doch bitte gleich mit std::make_unique, also auto pss = std::make_unique<TMemoryStream>();

    Muss der TMemoryStream überhaupt ein Pointer sein? Das klingt so, als würde der intern bereits rumpointern. Andererseits: diese T...-Typen, da weiß ich nie so genau, ist ja was C++-Builder spezifisches.



  • @wob

    Ja, muss es, leider. Alles, was von TObject erbt, kann man nicht auf dem Stack erzeugen.


  • Mod

    @DocShoe sagte in Beim TMemoryStream EAccessViolation -> Zugriffsverletzung:

    @wob

    Ja, muss es, leider. Alles, was von TObject erbt, kann man nicht auf dem Stack erzeugen.

    Ist das denn dann korrekt, da noch einen eigenen Auto-Pointer (oder manuelles delete) drumherum zu machen? Klingt doch sehr danach, als ob da eigene Ressourcenverwaltungsmagie passiert, mit entsprechend katastrophalen Folgen, wenn man sich da einmischt.



  • Doch, das ist leider immer noch der aktuelle Stand bei der VCL (entweder manuell delete aufrufen oder aber einen Smartpointer benutzen), selbst im zugehörigen Code-Beispiel von TMemoryStream wird in ComponentToString noch der std::auto_ptr<...>(...) (auch wenn dieser inzwischen veraltet ist) benutzt.



  • @SeppJ

    Kommt drauf an. Es gibt Objekthierarchien mit und ohne Ownership. Die mit Ownership zerstören ihre Objekte automatisch mit, wenn sie selbst zerstört werden, da reicht es aus, wenn man das top-level Objekt in einen smart pointer verpackt. Objekte ohne Ownership (zB. TMemoryStream) können ohne Probleme in smart pointer verpackt werden.
    std::make_sharedist aber generell problematisch, da Delphi Objekte nicht einfach per delete freigibt, sondern noch irgendwelchen Hokuspokus im Hintergrund veranstaltet (siehe hier: TComponent und make_unique/make_shared) und bei einem placement new in's Straucheln kommt.



  • Danke für die Antworten 😉

    war mein Fehler habe Versucht ein String in den MemoryStream zu Speichern und dann in ZipForge.

    Dann wieder auszulesen ging leider schief 😞

    std::auto_ptr<TMemoryStream> pss(new TMemoryStream());
    
    try { // __finally
    	try {
    
    		ZipForge1->FileName = UV_sArchivZIPFile;
    		ZipForge1->OpenArchive(fmOpenRead);		
    		ZipForge1->ExtractToStream("Datei", pss.get());
    
    	} catch (Exception& e) {
    		...
    	} catch (...) {
    		...
    	}
    
    } __finally {
    	ZipForge1->CloseArchive();
    }
    
    System::UnicodeString sBuffer = EmptyStr;
    	
    pss->Seek(0, soFromBeginning);
    		
    pss->Read(&sBuffer, pss->Size); <- Da kommt der Fehler
    

    Wen ich das so mache

    std::auto_ptr<TStringStream> pss(new TStringStream());
    
    ...
    
    sBuffer = pss->ReadString(pss->Size);
    

    geht es



  • @Rave1703 sagte in Beim TMemoryStream EAccessViolation -> Zugriffsverletzung:

    pss->Read(&sBuffer, pss->Size); <- Da kommt der Fehler

    Hast du ne Ahnung, was da passiert? Nicht, was passieren soll, sondern das, was tatsächlich passiert?
    Tipp: pss->Size ist nicht das Problem.



  • @DocShoe sagte in Beim TMemoryStream EAccessViolation -> Zugriffsverletzung:

    Hast du ne Ahnung, was da passiert? Nicht, was passieren soll, sondern das, was tatsächlich passiert?

    Hmm ... Ehrlich leider ne



  • @Rave1703 sagte in Beim TMemoryStream EAccessViolation -> Zugriffsverletzung:

    @DocShoe sagte in Beim TMemoryStream EAccessViolation -> Zugriffsverletzung:

    Hast du ne Ahnung, was da passiert? Nicht, was passieren soll, sondern das, was tatsächlich passiert?

    Hmm ... Ehrlich leider ne

    Ich steige hier zwar gerade quer ein, aber die Klasse UnicodeString sieht vermutlich so oder ähnlich aus:

    class UnicodeString
    {
       ...
       int length;
       WideChar* string_data;
       ...
    };
    

    Mit &sBuffer erhältst du die Adresse der obigen Datenstruktur, die wahrscheinlich nur wenige Bytes groß ist. Eine Schreiboperation auf diese (was du mit dem pss->Read(&sBuffer, ...) machst), wird also wahrscheinlich erstmal Länge, den Pointer auf die String-Daten und dann munter weiter den Stack überschreiben - z.B. irgendwelche nachfolgenden lokalen Variablen die da noch liegen.

    Der Effekt ist, dass du entweder mit der Schreiboperation über die letzte Speicherseite des Stack hinausschießt, dann gibt es sofort eine Zugriffsverletzung, oder dass danach im Stack nur noch Müll steht. Besonders in der Länge des Strings und dem string_data-Pointer. Beides wird mit dann hoher Wahrscheinlichkeit ebenfalls zu einer Zugriffsverletzung führen - spätestens wenn du irgendwas mit dem String machst - z.B. ihn ausgeben oder anderweitig lesen/schreiben (lesen von "kaputter" Speicheradresse in string_data oder "out of bounds" Read durch fehlerhafte length).

    Wenn du richtig Pech hast, merkst du auch erstmal gar nichts, weil du mit den fehlerhaften Speicherzugriffen in den Speicherseiten bleibst, die dein Prozess reserviert hat. Dann kann es sein, dass sich das Problem erst später und an einer ganz anderen Stelle manifestiert. Das ist dann kein Spaß zu debuggen 😁

    Was du wohl eigentlich machen wolltest, ist ein pss->Read(string_data, ...), da ich mich aber nicht wirklich mit VCL auskenne, weiß ich nicht genau wie man da ran kommt. Außerdem muss dabei sichergestellt werden, dass der reservierte Speicherbereich von string_data groß genug ist, um die Daten aufzunehmen - da weiß ich leider auch nicht ob ein UnicodeString auch so etwas wie std::string::reserve/resize kennt. Besser du bleibst also bei ReadString, das sieht erstmal okay aus - auch wenn es vielleicht nicht die effizienteste Lösung ist.



  • Ich idi durch die ganze Sache mit dem Neuen Rechner dache ich das es am Rechner liegt.
    Hätte ich auch darf kommen können, da ich ein Test mit 11 Byte gemacht habe da hat es funktioniert und
    bei ca. 2000 Byte kam der Fehler.

    Aber trozdem ... Danke dir


Anmelden zum Antworten