Von C# auf C++ DLL zugreifen - Methoden mit Referenzen



  • Hi DeathCubeK,

    wow, super! Das hat wieder funktioniert! Vielen Dank auch!

    Mit dem Pointer will ich auch nicht viel anfangen. Es soll nur der Zustand des Pointers gespeichert werden.

    Vielleicht als Hintergrund: meine ursprüngliche DLL ist eine 32 Bit DLL, die jetzt in einem 64 Bit Programm verwendet werden soll.
    Hierzu sollen die Funktionen der DLL über einen WCF Service bereitgestellt werden. Das 64 Bit Programm ist wieder in C++.



  • Almeida schrieb:

    Vielleicht als Hintergrund: meine ursprüngliche DLL ist eine 32 Bit DLL, die jetzt in einem 64 Bit Programm verwendet werden soll.
    Hierzu sollen die Funktionen der DLL über einen WCF Service bereitgestellt werden. Das 64 Bit Programm ist wieder in C++.

    Klingt nach ziemlich viel Aufwand. Kann man die Bibliothek nicht einfach nach 64-Bit portieren?

    (Sollte, sofern halbwegs "sauber" programmiert wurde und sofern man an den Quellcode heran kommt, ja nicht das große Problem sein)



  • Almeida schrieb:

    Hierzu sollen die Funktionen der DLL über einen WCF Service bereitgestellt werden. Das 64 Bit Programm ist wieder in C++.

    Dann implementier' doch gleich den Service-Contract in C++/CLI.



  • Hi zusammen,

    @DeathCubeK:
    leider kann man die Bibliotheken nicht nativ portieren. Dort hängt so viel uralter Kram mit dran, wo leider kein Zugriff auf die Sourcen besteht.
    Deine Einschätzung ist auch richtig. Es ist verdammt viel Aufwand, der noch dadurch erschwert wird, dass ich bisher noch kein CLI verwendet habe und WCF auch neues ist.

    @hustbaer:
    Du meinst den WCF-Service in C++/CLI schreiben? Mir wurde bisher überall empfohlen das nicht zu machen, da es über C# viel einfacher/schneller sein soll.
    Bist du da anderer Meinung?



  • Almeida schrieb:

    Soweit ich mich auskenne muss dieser Umweg sein, um eine unmanaged C++ DLL verwenden zu können.

    Nein, das muss man nciht verwenden.

    Ich würde ja immernoch dazu raten, C++/CLI komplett wegzulassen. Ich glaube schon dass der Service in C# einfacher umzusetzen ist, und die Anbindung an die native DLL ebenfalls. 🙂



  • DarkShadow44 schrieb:

    Ich würde ja immernoch dazu raten, C++/CLI komplett wegzulassen. Ich glaube schon dass der Service in C# einfacher umzusetzen ist, und die Anbindung an die native DLL ebenfalls. 🙂

    Wie greifst Du denn aus C# heraus direkt, d.h. ohne den Umweg über C++/CLI, auf eine "native" C++ (nicht C) Bibliothek zu? 😕

    Und damit meine ich wirklich dass Du eine native C++ Klasse, die aus der DLL exportiert wird, instantiieren willst, um anschließend Methoden des erzeugten Objekts aufzurufen. Vllt müssen sogar mehrere Objekte dieser Klasse erzeugt und verwaltet werden.

    Dass es mit reinen C Bibliotheken relativ einfach geht ist mir übrigens klar (keine Klassen, keine Namesppaces, kein Name-Mangling, etc). Mich würde daher die C#-Syntax für native C++ Bibliotheken/Klassen interessieren, sofern es das gibt...

    M$ selbst scheint hier den Weg über CLI/C++ vorzusehen:
    http://msdn.microsoft.com/en-us/library/ms235281.aspx



  • DeathCubeK schrieb:

    Wie greifst Du denn aus C# heraus direkt, d.h. ohne den Umweg über C++/CLI, auf eine "native" C++ (nicht C) Bibliothek zu? 😕

    Schau mal auf Codeproject - How to marshal a C++ class, da wird das ganz gut erklärt.
    Da die Dll ja nicht verändert werden darf, würde ich die Funktionen ganz einfach über den gemangelten Namen importieren, dann einen kleinen nativen Part der die Klassen erstellt/zerstört. Ok, auch das lässt sich in C# mache, aber wie verlässlich das ist kann ich dir nicht sagen. 😃

    class __declspec(dllexport) NativeClass
    {
    private:
    	int member1, member2;
    public:
    	NativeClass(int member2_new)
    	{
    		member1 = 42;
    		member2 = member2_new;
    	}
    
    	~NativeClass()
    	{
    	}
    	int getMember1(int mult)
    	{
    		return member1*mult;
    	}
    	int getMember2(int mult)
    	{
    		return member2*mult;
    	}
    };
    
    class Program
        {
            [DllImport("native.dll", EntryPoint = "?getMember1@NativeClass@@QAEHH@Z", CallingConvention = CallingConvention.ThisCall)]
            extern static int getMember1(IntPtr obj, int mult);
    
            [DllImport("native.dll", EntryPoint = "?getMember2@NativeClass@@QAEHH@Z", CallingConvention = CallingConvention.ThisCall)]
            extern static int getMember2(IntPtr obj, int mult);
    
            [DllImport("native.dll", EntryPoint = "??0NativeClass@@QAE@H@Z", CallingConvention = CallingConvention.ThisCall)]
            extern static int Contructor(IntPtr obj, int member2);
    
            [DllImport("native.dll", EntryPoint = "??1NativeClass@@QAE@XZ", CallingConvention = CallingConvention.ThisCall)]
            extern static int Destructor(IntPtr obj);
    
            static void Main(string[] args)
            {
                IntPtr obj = Marshal.AllocHGlobal(100); // Speicher anfordern der sicher größer ist als der den die Klasse braucht
                Contructor(obj, 33);
                int mem1 = getMember1(obj, 2);
                int mem2 = getMember2(obj, 2);
                Destructor(obj);
                Marshal.FreeHGlobal(obj);
            }
        }
    

    Finde ich persönlich die schönste Lösung, da komplett im managed Bereich gelöst. Allerdings wie gesagt leicht unschön.



  • Also, die "gemangelten" Methoden-Namen (einschließlich Konstruktor und Destruktor) alle von Hand importieren zu müssen und dann anschließend bei jedem Methoden-Aufruf einen nicht-typisierten Zeigen mit schleppen zu müssen, um den** thiscall zu emulieren, finde ich jetzt irgendwie alles andere als eine "schöne" Lösung. Das ist in etwa der Ansatz, mit dem man in reinem C "Klassen" nachbauen würde (Stichwort "C mit Klassen"). Fehleranfällig ist es allemal. Einmal den falschen IntPtr **übergeben und es kracht ziemlich sicher...



  • Natürlich verwendest du die Methoden so auch nicht direkt, sondern baust einen Wrapper dafür. 😉
    Dafür musst du den Service nicht in C++/CLI implementieren, und auch nicht eine weitere DLL verwalten die nur als Zwischenschicht dient.

    DeathCubeK schrieb:

    Also, die "gemangelten" Methoden-Namen (einschließlich Konstruktor und Destruktor) alle von Hand importieren zu müssen [...] finde ich jetzt irgendwie alles andere als eine "schöne" Lösung.

    Stimmt schon, wobei du dir die recht einfach rauskopieren kannst. Den Wrapper musst du ja auch so oder so von Hand schreiben. Solange sich die DLL nicht verändert bleiben die Namen ja gleich, du musst es also nur einmal machen. Kommt halt auch drauf an wie komplex die Klassen sind.

    Und nur weil es für mich die schönste Lösung ist heißt es ja nicht dass jeder so denken muss. In dem Link den ich gepostet habe stehen alle Möglichkeiten drin die man hat, jetzt musst der TS nur noch aussuchen was am besten passt. 🙂



  • Wenn Du eh eine Wrapper-Klasser baust, kannst Du das meiner Meinung nach in C++/CLI einfacher, sauberer und vor allem Typ-sicher machen. Die C++ Klasse lässt sich in C++/CLI ganz wie aus C++ gewohnt ansprechen, d.h. ohne dass man sich um Name-Mangling kümmern oder irgendwelche This-Pointer emulieren muss. Und nach außen hin lässt sich die Wrapper-Klasse dann wie eine "echte" C# Klasse benutzen. Aus meiner Sicht eine ziemlich Runde Sache. JNI, zum Beispiel, finde ich da deutlich umständlicher im Ansatz.

    Der Pferdefuß ist in der Tat dass man eine weitere DLL als "Zwischenschicht" bekommt. Da man aber sich aber mit der "nativen" DLL ohnehin externe Abhängigkeiten einhandelt, solle das in der Regel eher verschmerzbar sein...


Anmelden zum Antworten