Laden...

Wie aus einem unmanaged Bereich eines Array of Structs in Array in managed Bereich kopieren?

Erstellt von mifi261 vor 3 Jahren Letzter Beitrag vor 3 Jahren 1.165 Views
M
mifi261 Themenstarter:in
3 Beiträge seit 2020
vor 3 Jahren
Wie aus einem unmanaged Bereich eines Array of Structs in Array in managed Bereich kopieren?

Hallo zusammen,

ich möchte in C# eine Funktion aus einer C++ DLL aufrufen. Diese Funktion liefert mir einen Pointer auf Äußere Struktur zurück. Diese Äußere Struktur enthält wiederum ein Array bzw. Pointer auf eine Innere Struktur.

Meine Frage ist nun, wie kann ich die Daten des Array of Structs aus dem Unmanaged Bereich in ein Array of Struct in den Managed Bereich kopieren.
Folgend ist die Realisierung der Strukturen, sowie der Realisierte Aufruf der C++ Funktion und ein Lösungsvorschlag zum kopieren der Daten:


		[StructLayout(LayoutKind.Sequential)]
        private struct Innere
        {
            public IntPtr data;
            public int size;
            //Weitere Felder
        }
		
		[StructLayout(LayoutKind.Sequential)]
        rivate struct Aeusere
        {
            public Int32 iValue1;
            public Int32 iValue2;
            public double dValue;                               
        };
		
		[DllImport(DLLPfad, CallingConvention = CallingConvention.Cdecl)]
        private static extern void getData(IntPtr Data);
		
		main()
		{
			IntPtr pAeusere;
			
			getData(pAeusere);
			
			Aeusere aeusere = (Aeusere) Marshal.PtrToStructure(pAeusere, typeof(emxArray));
            
			// Bisheriger Versuch Daten zu kopieren
            Innere[] innere = new Innere[aeusere.size];
            for (int i = 0; i < aeusere.size; i++)
            {
                IntPtr pElement = new IntPtr(aeusere.data.ToInt32() + Marshal.SizeOf(typeof(Innere)) * i);
                Innere element =  (Innere) Marshal.PtrToStructure(pElement, typeof(Innere)); // Geht (Element enthält richtige Daten)
                innere[i] = element;   // Hier tritt Exception auf
            }
		}


Strukturen und Funktion in C++:



struct Innere
{
  int iValue1;
  int iValue2;
  double dValue;
};

struct aeusere
{
  Innere *data;
  int size;
  // Weitere Felder
};

DLL_EXPORT void getData(Aeusere* pAeusere)
{
	//Allokiere Speicherplatz für aeusere und Befülle aeusere
}


Beim bisherigen Versuch kann ich Problemlos die Außere Struktur auf meine C# Variable kopieren. Ebenso kann ich die Daten der Inneren Struktur auf meine Variable element kopieren.
(Im Überwachungsfenster beim Debuging werden mir die richtigen Werte innerhalb der Variablen element angezeigt.)

Erst bei der Zuweisung der Element Variable an das Array (innere) (im Code markiert) tritt eine mit folgendem Wortlaut Exception auf:
"Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt."

Ich verstehe jedoch nicht wieso die Variable element nicht zugewiesen werden kann? Im Überwachungsfenster ist sie ja schließlich befüllt und nicht "null".
Hatte jemand schon ein solches Problem oder kann jemand einen Fehler in meinem Programm feststellen?

S
248 Beiträge seit 2008
vor 3 Jahren

Hallo mifi261,

die Definitionen der structs im C# entspricht nicht denen aus dem C++ Code.

             IntPtr pElement = new IntPtr(aeusere.data.ToInt32() + Marshal.SizeOf(typeof(Innere)) * i);

Einen Pointer nach Int32 umzuwandeln ist keine gute Idee. Sollte dein Programm unter 64Bit laufen wirst du eine Exception bekommen. Verwende stattdessen besser den + Operator von IntPtr.

Aeusere aeusere = (Aeusere) Marshal.PtrToStructure(pAeusere, typeof(emxArray));

Wo ist emxArray definiert? Mir scheint hier, dass dies a) nicht kompiliert und b) zusammengewürfelte Teile sind.

Wenn du ein Array of structs übertragen willst, kannst du dies sehr viel einfacher machen indem du im C# direkt ein Array deiner Struct als Parameter angibst. Im C++ kopierst du dann einfach die Daten via memcpy in das Array.
Da du das Array natürlich im C# vorallokieren musst, und in C++ keine Länge da ist würde ich einen weiteren in/out Parameter für die Array-Länge anlegen:


        struct Innere
        {
            int iValue1;
            int iValue2;
            double dValue;
        };

        [DllImport(DLLPfad, CallingConvention = CallingConvention.Cdecl)]
        private static extern void GetData(Innere[] data, ref int size);

                int size = 0;
                Innere[] data = null;
                GetData(data, ref size); // Länge abrufen
                data = new Innere[size]; // Array allokieren
                GetData(data, ref size); // Werte direkt ins Array schreiben

C++


        DLL_EXPORT void GetData(Innere* array, int& size)
        {
            if (array == nullptr)
            {
                size = ...;//
            }
            else
            {
                memcpy(array, ..., sizeof(Innere) * size);
            }
        }

Damit kannst du dir die äußere Struct sparen und die Daten werden direkt kopiert also auch kein Marshalling nötig.

Grüße
spooky

M
mifi261 Themenstarter:in
3 Beiträge seit 2020
vor 3 Jahren

Danke für deine Antwort, bei mir im Programm haben die Strukturen und Variablen andere Namen. Beim kopieren habe ich wohl vergessen alle zu ändern, daher sollte "emxArray" eigentlich "Aeusere" heißen.

Leider kann ich im C++ Code nichts ändern, dieser ist fest vorgegeben.

M
mifi261 Themenstarter:in
3 Beiträge seit 2020
vor 3 Jahren

Habe meinen Fehler selbst gefunden, war ein Anfänger Fehler. Hatte vergessen den Speicher im Managed Bereich zu Allokieren. 😭

Habe nun "innere_ = new Innere();" in der For-Schleife eingefügt jetzt klappt alles wie gewünscht.


        main()
         {
             IntPtr pAeusere;

             getData(pAeusere);

             Aeusere aeusere = (Aeusere) Marshal.PtrToStructure(pAeusere, typeof(emxArray));

             // Bisheriger Versuch Daten zu kopieren
             Innere[] innere = new Innere[aeusere.size];
             for (int i = 0; i < aeusere.size; i++)
             {
                 IntPtr pElement = new IntPtr(aeusere.data.ToInt32() + Marshal.SizeOf(typeof(Innere)) * i);
                 Innere element =  (Innere) Marshal.PtrToStructure(pElement, typeof(Innere)); // Geht (Element enthält richtige Daten)
                 innere[i] = new Innere();  // Hier war der Fehler
                 innere[i] = element;   // Hier tritt Exception auf
             }
         } 

C
2.121 Beiträge seit 2010
vor 3 Jahren

Ich glaube nicht dass deine Lösung wirklich das Problem behoben hat.
Du erstellst zuerst ein neues Objekt (new Innere) und weist das an innere_ zu. Dann weist du etwas ganz anderes an innere_ zu. Das ist als wenn du schreibst
i = 4;
i = 5;

Die Zuweisung von 4 ist überflüssig, da die 4 gleich von der 5 überschrieben wird. In deinem Fall erstellst du ein Objekt (new Innere() ) ohne es zu verwenden.

S
248 Beiträge seit 2008
vor 3 Jahren

Das Problem ist, dass der Code hier vermutlich extrem ausgedünnt und dabei noch diverse Fehler enthält:


IntPtr pAeusere;
getData(pAeusere);

Alleine das kann schon nicht kompilieren.


Aeusere aeusere = (Aeusere) Marshal.PtrToStructure(pAeusere, typeof(emxArray));

Das wird zur Laufzeit krachen, außer Aeusere ist ein Alias für emxArray.

Was du machen möchtest ist relativ simpel zu lösen. Aber bitte zeige uns den richtigen Code und nicht etwas völlig verfälschtes.