C++ Interop Verständnis Problem



  • Hallo,
    Also eigentlich funktioniert der Code den ich weiter unter posten werden. Nur habe ich gewisse Probleme mit dem Verstehen.

    #include <iostream>
    
    using namespace std;
    using namespace System;
    using namespace System::Runtime::InteropServices;
    
    #pragma unmanaged
    void foo(char *bla)
    {
    	cout << "(unmanaged)" << bla << endl;
    }
    #pragma managed
    char* StringConv(String^ myString)
    {
    	IntPtr ip = Marshal::StringToHGlobalAnsi(myString);
    	return static_cast<char*>(ip.ToPointer());
    }
    
    int main()
    {
    	String^ managedString = gcnew String("Test string 1 2 3 ");
    
    	char* nativeString = StringConv(managedString);
    
    	foo(nativeString);
    
    	Marshal::FreeHGlobal((IntPtr)nativeString);
    }
    

    So jetzt zu meiner Frage(oder besser Fragen 🙂 ).
    Erstens, ist der Speicherplatz für den nativeString richtig wieder freigegeben wie ich es gemacht habe??? In der MSDN Hilfe macht er es ein bisschen anderst. Diese Lösung ist aber für mich nicht wirklich tauglich, da ich mit den IntPtr nicht speicher will.
    Zweitens, darf ich StringToHGlobal so verstehen, also ob hier ein new gefolgt von einer memcopy passiert???
    Drittens, ist FreeHGlobal sowas ähnliches wie delete??? Und warum wirft mir

    delete nativeString
    

    eine exception???

    Und noch eine Letzte die ein bisschen weniger mit dem Oben zu tun hat. Warum funktioniert das???

    String^ bla = gcnew String("bla bla");
    delete bla;
    Console::WriteLine(bla);
    


  • paindelux schrieb:

    Erstens, ist der Speicherplatz für den nativeString richtig wieder freigegeben wie ich es gemacht habe???

    Grundsätzlich ja. Ich würde den Zeiger zwar kapseln und an ein Objekt binden, welches vom Stack geräumt wird, aber das ist eine andere Sache.

    paindelux schrieb:

    Zweitens, darf ich StringToHGlobal so verstehen, also ob hier ein new gefolgt von einer memcopy passiert???

    Genauer: Ein AllocHGlobal gefolgt von einem memcpy gefolgt von einem Nullbyte 😉

    paindelux schrieb:

    Drittens, ist FreeHGlobal sowas ähnliches wie delete???

    Es ist eine Funktion, um mit der WinAPI-Funktion AllocHGlobal angeforderte Bereiche wieder freizugeben.

    paindelux schrieb:

    Und warum wirft mir

    delete nativeString
    

    eine exception???

    Weil Du nativeString nicht mit new, sondern mit AllocHGlobal allokiert hast 😉

    paindelux schrieb:

    Und noch eine Letzte die ein bisschen weniger mit dem Oben zu tun hat. Warum funktioniert das???

    String^ bla = gcnew String("bla bla");
    delete bla;
    Console::WriteLine(bla);
    

    Das ist eine Spezialität von C++/CLI: Wenn ein Objekt einen nichttrivialen Destruktor besitzt, implementiert der Compiler automatisch das IDisposable-Interface. Wenn Du jetzt ein mit gcnew angelegtes Objekt mit delete löscht, wird das Objekt ordnungsgemäß disposed. In anderen Sprachen (C#) würde man bla.Dispose() manuell aufrufen oder bla innerhalb eines using-Blocks verwenden.



  • Ok mal danke für die Antworten.
    aber ich muss nochmal kurz nachhaken.
    Mit Kapseln und an ein Objekt binden meinst du, einen smartPointer draus machen???

    Und zu der letzten antwort. Es geht bei dem letzten Codestückchen weniger um das delete, als mehr darum, dass ich danach noch auf das Objekt zugreifen kann (Sry war nicht ganz verständlich ausgedrückt).



  • Weil der SPeicher lediglich freigegeben wird. Du kannst in C++ beliebig auf Speicher zugreifen. Du darfst es aber nicht. Mal können da noch die alten Daten drinn stehen, mal steht da was völlig anderes im Speicher.

    Sobald Du etwas freigegeben hast, musst Du davon ausgehen, das der Speicherbereich schon für etwas anderes verwendet wird, ein Zugriff auf diesen ähnelt ein wenig Russisch Roulette



  • Hmm... das mag mit Unmanaged-Code so sein... aber bei managed-Code ist es nicht ganz so...

    Mit "delete" wird nur das "IDisposable" aufgerufen... und da "String" keines hat macht der Befehl gar nichts...

    Der Beispielcode macht folgendes:

    void main()
    {
    00000000  push        esi  
    00000001  cmp         dword ptr ds:[00952F10h],0 
    00000008  je          0000000F 
    0000000a  call        790B54C6 
    0000000f  xor         esi,esi 
    00000011  xor         esi,esi 
      String^ bla = gcnew String("bla bla");
    00000013  mov         eax,dword ptr ds:[022F3064h] 
    00000019  mov         esi,eax 
      delete bla;
      Console::WriteLine(bla);
    0000001b  mov         ecx,esi 
    0000001d  call        783DC2AC 
    }
    00000022  xor         eax,eax 
    00000024  pop         esi  
    00000025  ret
    

    Wie Du siehst macht das "delete bla" gar nichts 😉

    Siehe: Destructors and Finalizers in Visual C++
    http://msdn2.microsoft.com/en-us/library/ms177197



  • Ok, das wäre dann wohl die korrekte Antwort für die Frage.

    Man sollte dann aber erwähnen, das die Anahme sich auf eine spezielle Version der CLR, des Frameworks, der Zielplattform bzw. des Compilersstützt.

    Oder kurzum:

    Man sollte im Hinterkopf behalten das die Verwendung nach delete bla; Implementierungsdetails ausnutzt die sich ändern könnten.

    Es ist zwar unwarscheinlich das System.String plötzlich IDisposible implementiert, aber denkbar wäre das eine Implementierung eines Compilers bei einem delete auf System::String das Objekt als unerreichbar kennzeichnet so das der GC dies bei einem Collect aufräumt.

    Nach dem man ja den Assemblercode gesehen hat sieht man ganz deutlich warum man bei der Fragestellung auf das Objekt zugreifen kann (thx) , wer sich aber auf eine solche Implementierung verlässt riskiert IMHO letztendlich undefiniertes verhalten.


Anmelden zum Antworten