Übergabe einer Struktur als Referenz aus C# an eine C++/CLI-DLL.



  • Hallo Community,

    eine Frage:

    wie übergebe ich eine bestimmte Struktur als Referenz aus C# an eine C++/CLI-DLL, die wiederum die Funktion aus einer C++-DLL nutzt (wobei das Problem mit "hier Fehler" unten markiert ist)?

    Beispiel:

    Eine Struktur in cpp.h

    typedef struct
    {
        int x, y;
    }mystruct;
    

    Funktion in cppcli.cpp (DLL)

    #include "cpp.h"
    Klasse1::tauscheElemente(mystruct% s)
    {
        pin_ptr<mystruct> sptr = &s;
        tauscheElementeInCDLL(sptr);
    }
    

    Aufruf in C#

    struct myCSstruct
    {
    int x;
    int y;
    }
    
    myCSstruct mys;
    mys.x=10;
    mys.y=20;
    Klasse1.tauscheElemente(ref mys); //hier Fehler
    

    Danke für Eure Hilfe!!! 👍

    VG

    Paul



  • Mal ne Blöde Frage, kann es sein das du % mit & in deiner Funktion tauscheElement verwechselt hast?


  • Administrator

    1. Fehlermeldung angeben! Mit "hier Fehler" kann niemand etwas anfangen.
    2. Das Problem betrifft wiederrum eher C++/CLI, du bist somit erneut im falschen Forum. Ich werde dich gleich verschieben.
    3. Du kannst nicht eine normale C-Struktur als C#-Struktur verwenden. Siehe dazu am besten mal hier rein:
    http://msdn.microsoft.com/en-us/library/xey702bw.aspx
    bzw. weiterführend hier:
    http://msdn.microsoft.com/en-us/library/6w96b5h7.aspx

    Du musst somit trotzdem eine Managed Struktur anlegen, aber machst das Marshaling selber und hast dadurch die volle Kontrolle darüber.

    Grüssli



  • Dieser Thread wurde von Moderator/in Dravere aus dem Forum C# und .NET in das Forum C++/CLI mit .NET verschoben.

    Im Zweifelsfall bitte auch folgende Hinweise beachten:
    C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?

    Dieses Posting wurde automatisch erzeugt.



  • Firefighter schrieb:

    Mal ne Blöde Frage, kann es sein das du % mit & in deiner Funktion tauscheElement verwechselt hast?

    % ist eine Referenz auf ein verwaltetes Objekt. Bei der Deklaration der Parameter mit dem % in C++/CLI-Methode kann ich später im C#-Programm die Referenz einer Variablen oder eines Objekts (sind ja beide verwaltet) an die C++/CLI übergeben. Dabei muss ich beim Aufruf der Methode in C# vor der Variablen das Schlüsselwort "ref" angeben.

    Mit & deklariarte Parameter in C++/CLI erwartet die Methode in C# als Zeiger.



  • Dravere schrieb:

    2. Das Problem betrifft wiederrum eher C++/CLI, du bist somit erneut im falschen Forum. Ich werde dich gleich verschieben.

    Hi Dravere,

    da die C-DLL bereits fertig ist und ich keine Ahnung habe, wie ich diese in C# verwende, dachte ich, dass ich in C#-Forum richtig bin??? 😕



  • Bist nicht du der Autor von der C++/CLI DLL? Ich dachte, dass nur die C DLL fertig sei und du nun einen C++/CLI Wrapper baust, zumindest kam es mir so aus den anderen bisherigen Threads so vor.

    Heute morgen war ich noch ein klein wenig Müde und habe daher noch was anderes gar nicht gesehen. Wieso definierst du die Struktur in C# neu? Wenn du sie in C++/CLI bereits korrekt definiert hast, kannst du sie direkt in C# verwenden. Ein Marshaling zwischen der Un- und Managed-Welt muss du aber grundsätzlich immer vornehmen, da kommst du nicht drumherum.

    Grüssli



  • Ich glaube ich habe dich ein wenig verwirrt. Deshalb zur Ausgangssituation.

    Ich habe eine fertige C++-DLL inkl. Header und .lib (zur Anbindung an Profibus). In diesem Header sind verschiedene Strukturen beschrieben. Es gibt Funktionen in der C++-DLL, die z.B. einen Pointer auf eine Struktur erwarten.
    Außerdem habe ich ein Programm mit C++-Borland, in dem gewisse Abläufe definiert sind.

    Ziel ist: - ein Testprogramm für die Prüfung der Hardware, das über Profibus an PC angeschlossen wird, zu schreiben. Diese Prüfung sieht die Einbindung noch weiterer Geräte vor (z.B. über RS232 usw.). Es liegt außerdem ein SW-Projekt vor, dass mit C++-Borland geschrieben ist. Dort sind die Abläufe zur Ansteuerung der Profibusgeräte beschrieben.

    Ich habe einige Konzepte untersucht (soweit es die Zeit zugelassen hat) und bin zu dem Entschluss gekommen, die Abläufe wie z.B. Ansteuerung der Geräte über RS232 und grafische Aufbereitung der Daten mit C#, die Kommunikation über Profibus aber mit C++ zu schreiben, da ich die Header dort einbinden kann und die Abläufe aus Borland-Programm zum größten Teil übernehmen.

    Damit die Übertragung der Informationen zwischen C# und C++ ohne größere Anpassungen zu realisieren ist, möchte ich (auf deine Empfehlung) einen Wrapper auf Basis der CLI bauen (der gleichzeitig auch das Profibus-Programm ist).

    Und an dieser Stelle stoße ich auf einige Probleme. Unter anderem das mit den Strukturen:
    - C++-DLL erwartet Pointer vom Typ Struktur. Diese Struktur kann ich ja in C++/CLI über die Anbindung der Header-Datei bekannt geben. Aber wie teile ich C# diese Struktur mit?

    Ich hoffe, ich konnte mein aktuelles, von vielen anderen Problemen, deutlicher machen.

    VG

    Paul


  • Administrator

    Dann habe ich dich doch richtig verstanden. Du willst einen C++/CLI Wrapper bauen und hast Probleme mit C++/CLI. Dann bist du in diesem Forum doch richtig 🙂

    Zu deinem Problem:
    Wie ich schon sagte, um das Marshaling kommst du nicht herum. Du kannst C Strukturen nicht als Managed Strukturen verwenden und umgekehrt. Das geht auch in C++/CLI nicht. C++/CLI bietet eine erweiterte Syntax an, welche du verwenden solltest.
    In C++/CLI hast du die Möglichkeit das Marshaling selber zu übernehmen, wodurch nie ein Marshaler aus P/Invoke etwas über zu komplexe Strukturen meckern wird 😉
    c.h

    typedef struct
    {
      int x, y;
    }
    mystruct;
    

    cppcli.h

    value struct MyStruct
    {
      int x, y;
    }
    

    cppcli.cpp

    void Klasse1::tauscheElemente(MyStruct% s)
    {
      mystruct ms = { s.x, s.y };
      tauscheElementeInCDLL(&ms);
      s.x = ms.x;
      s.y = ms.y;
    }
    

    test.cs

    MyStruct mys = new MyStruct(); // <- Kommt direkt aus der C++/CLI DLL
    mys.x = 10;
    mys.y = 20;
    Klasse1.tauscheElemente(ref mys);
    

    So in der Art sollte es gehen, allerdings bin ich auch kein C++/CLI Platzhirsch, da haben wir eigentlich andere 😉

    Grüssli



  • Verstehe ich das richtig, dass in dem Wrapper folgende Schritte gemacht werden:
    1. eine nicht verwaltete Struktur wird erstellt
    2. Werte aus einzelnen Elementen der verwalteten Struktur (Eingangsparameter des Wrappers) werden in die Elemente der unverwalteter Struktur kopiert
    3. die Adresse der unverwalteter Struktur wird an die Funktion übergeben
    4. der Kopiervorgang von nicht verwaltet nach verwaltet

    D.h. bei sehr komplexen Strukturen (die ich im Projekt in Form eines Abbilds des Speichers habe) wird durch die Rumkopiererei die Performance so richtig in die Knie gehen.

    Gibt es da nicht eine Möglichkeit, dass ich einfach die Adresse von diesem Speicherabbild an C# übergebe und damit direkt auf den Speicher zugreife?

    Ich habe eine Variante, bei der ich mir jedoch unsicher bin, ob diese auf Dauer funktioniert (bis der GC irgendwann mal Weihnachtsputz macht). Hier ist meine Variante. Bitte um Kommentare, ob diese auch richtig ist, funktionieren tut sie ja.

    //Cstrukturen.h
    #include "stdafx.h"
    
    #ifndef STRUKTUREN_H
    #define STRUKTUREN_H
    typedef struct 
    {
    	int x;
    	int y;
    } einfacheCStruktur;
    
    #endif /* STRUKTUREN_H */
    
    //C-DLL.cpp
    #include "Cstrukturen.h"
    EINSTIEG void FUNK_AUFRUF_KONV fuelleStrukturElemente2( einfacheCStruktur * a )
    	{
    		//hier kann direkt auf die Einzelelemente zugegriffen werden
    		//der Cast (einfacheStruktur*) strukturiert den Speicher ab der
    		//Adresse, die mit a übergeben wurde
    		a->x = 1000;
    		a->y = 2000;
    	}
    
    //CLIStrukturen.h
    #include "stdafx.h"
    
    public value struct einfacheCLIStruktur
    {
    	int x, y;
    };
    
    //CLR-DLL.cpp
    #include "CStrukturen.h"
    void CLRDLL::Funktionen1::clrFuelleStrukturElementeMitNullPtr2( void * s)
    {
    	//Variante 2:
    	//1. CLI-DLL empfängt einen neutralen Pointer (in der Tat ist es die Adresse
    	//einer Struktur, die in C# deklariert wurde)
    	//2. deklariert einen Pointer des Typs "einfacheStruktur" und übergibt diesem
    	//die Adresse der empfamgener Struktur
    	//3. der Pointer wird an die C-DLL weitergegeben
    	einfacheCStruktur * es;
    	es = (einfacheCStruktur*)s;
    	fuelleStrukturElemente2(es);
    }
    
    //Test.cs
    private void button8_Click(object sender, EventArgs e)
            {
                einfacheCLIStruktur s; //<- ist in C# bekannt, weil in C++/CLI-DLL public
                unsafe
                {
                    void* nullptr = &s;
                    Funktionen1.clrFuelleStrukturElementeMitNullPtr(nullptr);
                }
                listBox1.Items.Add(s.x.ToString() + " / " + s.y.ToString());
            }
    

    Eine weitere Frage hierzu: kann ich nicht einfach den vorhandenen Header, der mit der C-DLL mitgeliefert ist in C++/CLI-Projekt integrieren (also nicht per include, sondern reinkopieren), ihn public machen und somit C#-Programm zur Verfügung stellen? Kann ich diese C++-Strukturen (nicht C++/CLI) in C# nicht verwenden?

    VG

    Paul



  • Du kannst nur direkt kopieren mit pinning und structs mit SequenticalLayout.
    Ansonsten kommst Du um ein händisches Kopieren nicht herum.



  • Danke für den Tipp!


Anmelden zum Antworten