struct-Array an C++-DLL übergeben



  • _matze schrieb:

    Ok, das sorgt tatsächlich dafür, dass ich die Daten zumindest verarbeiten kann. Allerdings wird nun logischerweise eine Kopie erzeugt (ist 'ne andere Speicheradresse). Ich müsste das Array eigentlich per Referenz übergeben, da an der aufrufenden Stelle noch mit den kalkulierten Daten gearbeitet werden muss. Gibt es da keine Möglichkeit?

    Hm, eigentlich sollte es so gehen. Du veränderst ja nicht das Array (= den Zeiger) sondern die enthaltenen Daten.



  • Laut einem Kollegen verarbeitet C# solche Arrays of structs wohl anders als C++, nämlich nicht als zusammenhängenden Speicherblock, sondern als Array of Pointers to Structs, wenn ich das richtig verstanden habe (und wenn es stimmt). Das wäre zumindest eine Erklärung für meinen Datensalat => ich sehe eigentlich Speicheradressen. Eine andere Erklärung habe ich nicht, ein double ist in C# ebenso 64 Bit groß, die Speicheradresse von pColors ist auf der C++ dieselbe wie vorher in C#, und schon der erste Wert stimmt nicht überein. Eine Lösung weiß der Herr Kollege allerdings auch nicht...



  • _matze schrieb:

    Laut einem Kollegen verarbeitet C# solche Arrays of structs wohl anders als C++, nämlich nicht als zusammenhängenden Speicherblock, sondern als Array of Pointers to Structs

    Nein, das ist falsch. .NET unterscheidet zwischen Werte- und Referenztypen. Referenztypen werden in der Tat wie beschrieben verwaltet (das bezieht sich dann aber nicht nur auf Arrays). Structs hingegen sind immer Wertetypen und entsprechen (für solche Belange) ziemlich genau den ganz normalen C++-Datentypen (wohingegen Referenztypen eine Art verwalteter Zeiger sind).



  • gut, ich kriege es so wohl nicht hin. Ich werde als nächstes versuchen, das Array aufzusplitten in 6 double-Arrays. Vielleicht habe ich da mehr Erfolg... Zumindest einzelne int und double Werte per Referenz waren bislang kein Problem...



  • Aber ist ein Array von Wertetypen nicht selbst ein Referenztyp?



  • ich hatte mal ein ähnliches problem, finde den code aber leider nicht mehr. ich kann mich wage erinnern einen struktur prototypen als class in c# deklariert zu haben, welche das selbe layout wie die c struktur besaß (besitzte? :s). an die importierte c funktion habe ich dann einen IntPtr auf eine instanz dieser klasse übergeben.



  • sothis_ schrieb:

    ich hatte mal ein ähnliches problem, finde den code aber leider nicht mehr. ich kann mich wage erinnern einen struktur prototypen als class in c# deklariert zu haben, welche das selbe layout wie die c struktur besaß (besitzte? :s). an die importierte c funktion habe ich dann einen IntPtr auf eine instanz dieser klasse übergeben.

    Interessant! Werde ich morgen ausprobieren. Jetzt ist erstmal Feierabend! 😃



  • LordJaxom schrieb:

    Aber ist ein Array von Wertetypen nicht selbst ein Referenztyp?

    Ja, das ist richtig. Daher ist auch die explizite ref -Übergabe unnötig. Eventuell marshallt sich .NET da Mist zusammen, aber eigentlich sollte das für so'n Vanilla-Array wunderbar funktionieren.

    Ansonsten versuch's halt mal mit [MarshalAs(UnmanagedType.LPArray)] aber das sollte nichts ändern.



  • sothis_ schrieb:

    ich hatte mal ein ähnliches problem, finde den code aber leider nicht mehr. ich kann mich wage erinnern einen struktur prototypen als class in c# deklariert zu haben, welche das selbe layout wie die c struktur besaß (besitzte? :s). an die importierte c funktion habe ich dann einen IntPtr auf eine instanz dieser klasse übergeben.

    Das könnte sogar funktionieren, wenn man aufpasst, es ist aber riskant, und codelastig. Man muss sich nämlich einen pinned-Pointer besorgen, damit der GC nicht anfängt, die Klasse plötzlich woanders hinzuräumen. Außerdem ist es mit Kanonen auf Spatzen, denn hier soll ja mit Arrays gehandelt werden.



  • Konrad Rudolph schrieb:

    sothis_ schrieb:

    ich hatte mal ein ähnliches problem, finde den code aber leider nicht mehr. ich kann mich wage erinnern einen struktur prototypen als class in c# deklariert zu haben, welche das selbe layout wie die c struktur besaß (besitzte? :s). an die importierte c funktion habe ich dann einen IntPtr auf eine instanz dieser klasse übergeben.

    Das könnte sogar funktionieren, wenn man aufpasst, es ist aber riskant, und codelastig. Man muss sich nämlich einen pinned-Pointer besorgen, damit der GC nicht anfängt, die Klasse plötzlich woanders hinzuräumen. Außerdem ist es mit Kanonen auf Spatzen, denn hier soll ja mit Arrays gehandelt werden.

    in erster linie ging es doch um strukturen? iirc hatte ich die lösung auch irgendwo von einem microsoft forum und schien die 'richtige' zu sein wenn man zeiger/referentzen auf structs zwischen managed und unmanaged code übergibt. dies wie auch immer funktioniert aber tatsächlich nur wenn in der betreffenden struktur generische datentypen vorhanden sind, die sich 1:1 von c/c++ auf c# abbilden lassen, also z.B. int/Int, oder bool/Bool.

    edit: oh, und was meinst du mit codelastig? viel zu schreiben war es jedenfalls nicht 🙂



  • sothis_ schrieb:

    Konrad Rudolph schrieb:

    sothis_ schrieb:

    ich hatte mal ein ähnliches problem, finde den code aber leider nicht mehr. ich kann mich wage erinnern einen struktur prototypen als class in c# deklariert zu haben, welche das selbe layout wie die c struktur besaß (besitzte? :s). an die importierte c funktion habe ich dann einen IntPtr auf eine instanz dieser klasse übergeben.

    Das könnte sogar funktionieren, wenn man aufpasst, es ist aber riskant, und codelastig. Man muss sich nämlich einen pinned-Pointer besorgen, damit der GC nicht anfängt, die Klasse plötzlich woanders hinzuräumen. Außerdem ist es mit Kanonen auf Spatzen, denn hier soll ja mit Arrays gehandelt werden.

    in erster linie ging es doch um strukturen?

    Ja. Und? Zusammenhang?

    iirc hatte ich die lösung auch irgendwo von einem microsoft forum und schien die 'richtige' zu sein wenn man zeiger/referentzen auf structs zwischen managed und unmanaged code übergibt.

    Das kann ich mir nicht vorstellen, denn genau dafür ist ja das (automatische!) Marshalling zwischen Managed und Unmanaged Code da.

    edit: oh, und was meinst du mit codelastig? viel zu schreiben war es jedenfalls nicht 🙂

    Na doch, da Du ja eine unsafe-Methode hast, in der Du zuerst den Zeiger pinnen musst, hast Du auf jeden Fall mehr zu schreiben (= mehr Möglichkeiten, Fehler zu machen) als wenn Du die Methode direkt aufrufst.



  • Sollte man nicht auch immer diese "layout sequential" Property bei der Struktur mit angeben wenn man die mit PInvoke verwenden will?



  • hustbaer schrieb:

    Sollte man nicht auch immer diese "layout sequential" Property bei der Struktur mit angeben wenn man die mit PInvoke verwenden will?

    Ja, das ist in der Tat sauberer, hat aber mit dem Problem nichts zu tun, da ab Visual Studio 2005 (bzw. der damit verbundenen Version der Compiler) das Sequential-Layout die Standardeinstellung ist.



  • Konrad Rudolph schrieb:

    hustbaer schrieb:

    Sollte man nicht auch immer diese "layout sequential" Property bei der Struktur mit angeben wenn man die mit PInvoke verwenden will?

    Ja, das ist in der Tat sauberer, hat aber mit dem Problem nichts zu tun, da ab Visual Studio 2005 (bzw. der damit verbundenen Version der Compiler) das Sequential-Layout die Standardeinstellung ist.

    Ich nutze noch das VS2005. Also könnte das was mit dem Problem zu tun haben?



  • Nö daran wirds nicht liegen wenn das sowieso ab 2005 Standard ist (wusste ich nicht, obwohl ich 2005 verwende 🙂 ).
    Wenn du in/out brauchst kann ich dir leider auch nicht helfen.



  • hustbaer schrieb:

    Nö daran wirds nicht liegen wenn das sowieso ab 2005 Standard ist (wusste ich nicht, obwohl ich 2005 verwende 🙂 ).
    Wenn du in/out brauchst kann ich dir leider auch nicht helfen.

    Jetzt macht meine Frage auch keinen Sinn mehr. Gestern stand bei Konrad Rudolphs Post auch noch 2008... 😃



  • _matze schrieb:

    hustbaer schrieb:

    Nö daran wirds nicht liegen wenn das sowieso ab 2005 Standard ist (wusste ich nicht, obwohl ich 2005 verwende 🙂 ).
    Wenn du in/out brauchst kann ich dir leider auch nicht helfen.

    Jetzt macht meine Frage auch keinen Sinn mehr. Gestern stand bei Konrad Rudolphs Post auch noch 2008... 😃

    Sorry, das ist richtig. Ich hatte den Fehler in meinem Posting gleich bemerkt und editiert, daher habe ich mir den „Edit“-Hinweis geschenkt. Du bist mit Deiner Frage anscheinend genau so dazwischengekommen, dass Du noch die falsche Zahl gelesen hast, aber schon die richtige zitiert.



  • Hallo,
    ich grab diesen Thread nochmal aus. Vielleicht könnte Matze ja mal schreiben wie er es letztendlich umgesetzt hat.
    Ich bin nämlich gerade an dem gleichen Problem. Bei einer Übergabe des Struktur-Arrays kann ich im C-Programm zwar lesen, aber nicht schreiben.

    Zum Testen habe ich mir jetzt folgendes geschrieben:

    struct myStruct {
    	int val;
    	char data[20];
    };
    
    int printMyStructArray(struct myStruct myStr[], int anz){
    	int i;
    	myStr[1].val = 987; //TEST schreiben
    
    	for (i = 0; i < anz; i++) {
    		printf("\nmyStruct[%d].val = %d\n", i, myStr[i].val);
    		printf("myStruct[%d].data = %s\n", i, myStr[i].data);
    	}
    	return anz;
    }
    

    Das Ganze in eine DLL gepackt und im C#-Programm folgende Importbeschreibung:

    [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
        private struct myStruct
        {
            public Int32 val;
            [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 20)]
            public string data;
        }
    
        // int printMyStructArray(struct myStruct myStr[], int anz);
        [DllImport("mingw_dll.dll")]
        private static extern int printMyStructArray(
            myStruct[] myStr,
            int anz
         );
    

    Und zum ausprobieren folgendes:

    myStruct[] myStr = new myStruct[20];
    myStr[0].val = 12;
    myStr[0].data = "Dies";
    myStr[1].val = 34;
    myStr[1].data = "ist";
    myStr[2].val = 56;
    myStr[2].data = "ein";
    myStr[3].val = 78;
    myStr[3].data = "Test";
    Console.WriteLine("Vorher:");
    Console.WriteLine("myStr[1].val = " + myStr[1].val);
    ret = printMyStructArray(myStr, 4);
    Console.WriteLine("Nachher:");
    Console.WriteLine("myStr[1].val = " + myStr[1].val);
    Console.WriteLine("returned: " + ret);
    

    Ausgabe des Programmes:

    Vorher:
    myStr[1].val = 34
    
    myStruct[0].val = 12
    myStruct[0].data = Dies
    
    myStruct[1].val = 987
    myStruct[1].data = ist
    
    myStruct[2].val = 56
    myStruct[2].data = ein
    
    myStruct[3].val = 78
    myStruct[3].data = Test
    Nachher:
    myStr[1].val = 34
    returned: 4
    

    Das Lesen geht also, nur das Schreiben nicht.

    Oben wurde ja mal geschrieben dass man das Problem lösen kann indem man die Struktur in eine extra Klasse packt. Gibt es dazu vielleicht ein Beispiel, oder eine andere Lösung?

    Gruß
    Thomas



  • Für mein konkretes Testbeispiel konnte ich das Problem mittels des Out-Attributes lösen:

    [DllImport("mingw_dll.dll")]
    private static extern int printMyStructArray([In, Out] myStruct[] myStr, int anz);
    

    Link zur MSDN:
    http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.outattribute.aspx

    Gruß



  • Hi,

    ich hatte genau das gleiche Problem, vielen Dank fuer die [In,Out] Loesung !!!
    Funktioniert !


Anmelden zum Antworten