C++/native C++/CLI marshalling IntPtr

Ein IntPtr gibt den Speicher nicht frei, auf den er zeigt. Wird er also geGCt wird lediglich sein eigener Speicher freigegeben, nicht der auf den er sozusagen zeigt. Von daher gesehen kann die GC einen Handle, der in einen IntPtr gepackt wurde nicht freigeben (im Gegensatz zu SafeHandle, das genau dafür konzipiert wurde).

es geht aber nicht um das Freigeben sondern darum, das die GC auch Speicher defragmentiert, was konkret bedeutet das Objekte auf dem Heap herumgeschoben werden können. Reicht man nun eine Variable/Zeiger aus einem Managed Objekt runter in eine native Funktion und die GC verschiebt das managed Objekt _bevor_ die native Funktion beendet, dann ist die Variable/Zeiger der nativen Funktion nicht mehr gültig -> Stack bzw heap Korruption. Dies gilt übrigends auch (!) für variablen die by Value übergeben werden, auch wenn man das auf den ersten Blick nicht erwarten würde.

Also sowas wie:
Code:
void managed_function()
{
     int wert = 5;

     unmanaged_function(wert); // kann heap corruption erzeugen
}

sollte eigendlich lauten:

Code:
void managed_function()
{
     int wert = 5;
   
   pin_ptr<int> pinnedWert = &wert;

     unmanaged_function(*pinnedWert); // sicher
}

Das dumme an diesem Fehler ist, daß man zu 99% damit durchkommt weil er nur dann auftritt wenn die GC zufällig zum richtigen Zeitpunkt läuft um den Fehler zu verursachen... Die Fehlerhäufigkeit steigt meist mit der Anzahl der GC-Läufe.
 
Vielen Dank, Cromon,
gute Antwort, das passt für meinen kleinen Explorer soweit.
So ein klein wenig Feedback muss mal sein ;)
jsendrow, guter Vorschlag, aber das Pointer-Pinnen musste ich zum Glück bisher nie verwenden bei InterOp, das wäre auch ein Schritt zurück zu native C++.
Die GC und IJW sind das einzige, was mich noch bei C++/CLI hält.
 
Er will ja nicht den IntPtr an eine unverwaltete Funktion übergeben, was man über P-Invoke aber auch problemlos machen könnte, sondern er will einen aus einer unverwalteten Funktion erhaltenen Zeiger in einem IntPtr speichern und den dann im verwalteten verwenden. Und das geht problemlos.
 
Danke dir für die Info, sowas ist mit dem Debugger natürlich nicht zu tracken. Falls du mal Zeit und Lust hättest wäre das ein guter Tutorialeintrag. Es gibt sicher eine menge Leute, die mit Native C++ Refactoring beschäftigt sind und genau solche Probleme haben.
So what, danke jsendrow.

Das dumme an diesem Fehler ist, daß man zu 99% damit durchkommt weil er nur dann auftritt wenn die GC zufällig zum richtigen Zeitpunkt läuft um den Fehler zu verursachen... Die Fehlerhäufigkeit steigt meist mit der Anzahl der GC-Läufe.

Er will ja nicht den IntPtr an eine unverwaltete Funktion übergeben, was man über P-Invoke aber auch problemlos machen könnte, sondern er will einen aus einer unverwalteten Funktion erhaltenen Zeiger in einem IntPtr speichern und den dann im verwalteten verwenden. Und das geht problemlos.

jawoll, genau so will ich das, und genau so funktioniert es auch (auf meinem Rechner). Ohne PInvoke (spuck auf den Boden), etwas marshalling, etwas casten und IJW.
Nur die Sache mit dem Native-Zeiger-Pinnen, die ist noch etwas unklar. Nehmen wir mal an, ich müsste aus CLI auf einen Native-Stuct zugreifen, die mir eine Win32-Api Fkt. liefert. Reicht es dann aus, einen Pointer darauf zu setzen, dachdem ich den Scope verlassen habe?
Also Wrapper macht Win32-Call -> bekommt by val eine struct -> ich liefere einen Zeiger innerhalb der Struct zurüch an meine App.
 
Danke dir für die Info, sowas ist mit dem Debugger natürlich nicht zu tracken. Falls du mal Zeit und Lust hättest wäre das ein guter Tutorialeintrag. Es gibt sicher eine menge Leute, die mit Native C++ Refactoring beschäftigt sind und genau solche Probleme haben.
So what, danke jsendrow.

Tell me about it. es hat mich fast einen Monat debugging und Haare ausreissen gekostet weil ich meine gelegentlichen Heap-Corruptions so absolut gar nicht eingrenzen konnte, denn der Fehler ist tatsächlich im Debugger gar nicht sichtbar, bzw. man bekommt dauernd angeblich kaputte Objekte angezeigt von denen man 100%tig weis das sie nicht kaputt sein können. Schlimmer noch, jeh näher ich die Fehlerquelle eingrenzen konnte, desto häufiger und gleichzeitig unverständlicher wurde er... Am Ende konnte ich die Fehler durch eine erzwunge GC zum "richtigen" Zeitpunkt zwar zu 100% reproduzieren, aber in keinester Weise erklären... Dann habe ich das mit pin_ptr gelernt... Code umgestellt auf das Beispiel mit pin_ptr und der Fehler war beseitigt.

Du pinnst nicht den native Zeiger, sondern Du pinnst das managed objekt in dem der Zeiger existiert.

Ein solches Objekte kann von der GC nicht verschoben werden solange es gepinned ist. Und genau darum geht es.

Das Problem mit dem nativen Zeiger ist, das dieser für die GC unsichtbar ist. Wäre das nicht der Fall könnte IJW den Zeiger einfach umbiegen und alles wäre gut. Aber genau das war halt technisch (bisher) nicht machbar.
 
Alles Gepinne ist lediglich nötig, wenn du die Adresse eines verwalteten Objekts an eine unverwaltete Funktion geben willst. Und für diese Aktion gibt es grundsätzlich P-Invoke, da wird nämlich automatisch gepinnt vor dem Funktionsaufruf.
 
Alles Gepinne ist lediglich nötig, wenn du die Adresse eines verwalteten Objekts an eine unverwaltete Funktion geben willst. Und für diese Aktion gibt es grundsätzlich P-Invoke, da wird nämlich automatisch gepinnt vor dem Funktionsaufruf.

Wir reden hier aber ja auch nicht über PInvoke ;) Das, nebenbei, eine erhebliche Performance-Penalty mit sich bringt. Die MSDN spricht hier von einem Overhead im Bereich 50-80 Befehle pro PInvoke, finde leider grad die Quelle dazu nicht mehr.
 
Wir reden ja aber auch gem. dem Threadersteller nicht davon verwaltete Objekte in unverwaltete Funktionen zu übergeben, sondern unverwaltete Objekte in einem IntPtr zu speichern. Und das gibt überhaupt keine Probleme, da die Member des IntPtr (bzw der Member) nicht verschoben werden.
 
Alles Gepinne ist lediglich nötig, wenn du die Adresse eines verwalteten Objekts an eine unverwaltete Funktion geben willst.

Das ist so nicht ganz richtig, bzw durch IJW werden auch solche Sachen zu Addressen die man auf den ersten Blick nicht als solche erkennt. Beispiel:

Code:
ref class Test
{
    int Wert;  // Membervariable einer managed Klasse

   void Funktion()
   {
        UnmanagedFunktion(Wert);  // Fehler
   }
};

Auf den ersten Blick übergibtst Du hier eine Variable by Value, was ja keine Adresse eines Objektes wäre. Aber, diese Variable "lebt" auf dem managed heap innerhalb der virtuellen Machine. Tatsächlich übergibt IJW hier nicht die variable, sondern den Zeiger auf die VAriable innerhalb des managed heap an eine native Funktion. Würde jetzt die GC genau dieses Objekt verschieben während die UnmanagedFunktion ausgeführt wird, dann wäre "Wert" an einer anderen Stelle im managed Heap, der Zeiger in den die UnmanagedFunktion reinschreibt also nicht mehr gültig -> bumm.

Und das ist auch für Traveller nicht ganz unrelevant, weil er native Funktionen aufruft denen er ja auch Werte übergibt auf die das zutreffen könnte.
 
Wir reden hier aber ja auch nicht über PInvoke ;) Das, nebenbei, eine erhebliche Performance-Penalty mit sich bringt. Die MSDN spricht hier von einem Overhead im Bereich 50-80 Befehle pro PInvoke, finde leider grad die Quelle dazu nicht mehr.

Ja genau, mit dem manged C++ ist das üble PInvoke ganz gut zu umgehen, das Marshalling ist aber zu beherrschen, und genau da bin ich die Theorie betreffend etwas hilflos, in der Praxis funktioniert es, es gibt auch nicht allzuviele Fehler, die man abfangen muss. Ich bin scheinbar auf dem besten Weg tief in die Win32-Api einzutauchen, die bösen windows.h-datentypen inclusive. Aber was solls, die Win-Api ist gut dokumentiert, und meine IntPtr's werden's schon richten.

Wenn ihr mehr Interop-Tips habt, dann könnt ihr die gerne als Antwort posten.

Wir reden ja aber auch gem. dem Threadersteller nicht davon verwaltete Objekte in unverwaltete Funktionen zu übergeben, sondern unverwaltete Objekte in einem IntPtr zu speichern. Und das gibt überhaupt keine Probleme, da die Member des IntPtr (bzw der Member) nicht verschoben werden.

Ja durchaus, obwohl der umgekehrte Weg auch interessant wäre
 
Zurück