SOLVED - Funktion (in C#) aus DLL (c++) ausführen



  • Danke, funktioniert traumhaft.

    Folgende Implementierung

    In C#:
    Main.cs

    int main(){
    /*...*/
    CMessageCallback.InitializeMessageCallBack();
    CMessageCallback.TestMessageCallBack();
    return 0;
    }
    

    CMessageCallback.cs

    using System.Runtime.InteropServices;
    
        static class CMessageCallback
        {
            [UnmanagedFunctionPointer(CallingConvention.StdCall)]
            public delegate void MessageCallback(IntPtr Text);
    
            [DllImport(GlobalVariables.ReadMeshKernel_LIBPATH + "MyDLL.dll", EntryPoint = "InitializeMessageCallBack", CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
            static extern void InitializeMessageCallBackExtern([MarshalAs(UnmanagedType.FunctionPtr)] MessageCallback callbackPointer);
    
            [DllImport(GlobalVariables.ReadMeshKernel_LIBPATH + "MyDLL.dll", EntryPoint = "TestMessageCallBack", CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
            static extern void TestMessageCallBack();
    
            public static MessageCallback MessageCallBack =
                (Text) =>
                {
                    Output.LogOut(Marshal.PtrToStringAnsi(Text));
                };
    
            public static void InitializeMessageCallBack()
            {
                InitializeMessageCallBackExtern(MessageCallBack);
            }
        }
    

    In der C++-DLL:
    MessageCallBack.h

    #define MESSAGE_CALLBACK_H
    
    typedef void(__stdcall * MessageCallback)(char*);
    
    extern MessageCallback MessCallBack;
    
    namespace MessageCallBack {
    	extern "C" __declspec(dllexport) void InitializeMessageCallBack(MessageCallback CallBack) { if (CallBack != NULL) { MessCallBack = CallBack; } }
    
    	void DoMessageCallBack(char* Text) {
    		if (MessCallBack != NULL) { MessCallBack(Text); }
    	}
    
    	extern "C" __declspec(dllexport) void TestMessageCallBack() { DoMessageCallBack("Message callback test!"); }
    }
    

    Main.cpp

    #ifndef MESSAGE_CALLBACK_H
        #include "MessageCallBack.h"
    #endif
    
    static MessageCallback MessCallBack = NULL;
    

    Jetzt kann in der DLL jederzeit die Funktion MessCallBack("Text!") aufgerufen werden und der Inhalt wird in meiner GUI auf der ListView ausgegeben 🙂

    So sollte es sein. Vielen Dank für den Hinweis.

    Ursprüngliche Nachricht:

    Hallo.

    Ich habe ein Programm, dass in einer DLL läuft. Die DLL habe ich in C++ geschrieben. Die dazugehörige GUI habe ich in C# geschrieben. Es ist überhaupt kein Problem, aus C# eine Funktion der DLL auszuführen. Aber geht das auch umgekehrt? Wenn die DLL z.B. ein Objekt erzeugt möchte ich, dass dieses der GUI mitgeteilt wird. Also konkret, wenn die DLL z.B eine Objekt vom Typ CPoint (eine selbstdefinierte Klasse) erzeugt, soll dieses Objekt im TreeView meines C# Programms angezeigt werden. Die Klassen stark vereinfacht dargestellt:

    class CPoint{
    private:
        double x, y, z;
    
    public:
        CPoint(){
            this->x = .0;
            this->y = .0;
            this->z = .0;
        }
    
        void SetPoint(double fx, double fy, double fz){
           this->x = fx;
           this->y = fy;
           this->z = fz;
        }
    }
    
    public class CPoint{
        private double x;
        private double y;
        private double z;
    
        public CPoint(){
            this.x = .0;
            this.y = .0;
            this.z = .0;
        }
    
        public void SetPoint(double fx, double fy, double fz){
           this.x = fx;
           this.y = fy;
           this.z = fz;
        }
    }
    
    public class CPointTreeNode : TreeNode{
        private CPoint P;
    
        public CPointTreeNode(CPoint fP){this.P = fP;}
    }
    

    Über einen Timer immer wieder die DLL zu fragen, obs was Neues gibt, finde ich sehr unelegant. Das Programm ist schon recht fortgeschritten (1000 Seiten!!) und so eine Funktionalität wäre sehr sehr gut für mich. Also sagt bitte nicht einfach Nein, wenn es irgendeine Möglichkeit, sei sie noch so abstrakt, dafür gibt. Ein Stichwort würde mir auch erstmal reichen, dann Google ich das und schau mir das selbst an und melde mich ggf. wieder - ein Beispiel wäre natürlich super.

    Qualität geht vor "Codekürze".

    Grüße,
    CJens



  • Nein, direkt geht das nicht (außer du verwendest C++/CLI).

    Deklariere und benutze doch einfach in der C++ Funktion eine Callback-Funktion, welche dann vom C#-Programm aus mitgegeben wird (mittels Callback-Marshalling: How to make a callback to C# from C/C++ code).



  • Stichwörter zu dem Vorschlag von Th69:
    PInvoke, delegate, Marshalling

    Und noch, weil für Anfänger aus dem Beitrag von Th69 vielleicht nicht sofort ersichtlich, der Hinweis: Du musst* das "verkehrt rum" angehen. D.h. du rufst erstmal aus C# heraus eine C bzw. C++ Funktion auf, der du einen Delegate mitgibst über den sie "zurückrufen" kann. Den merkt sich der C bzw. C++ Code dann, und ruft ihn halt eben auf wenn es was mitzuteilen gibt.

    Dabei solltest du natürlich beachten dass der Delegate ein Objekt ist, welches wie alle anderen der Garbage-Collection unterliegt. Der .NET Teil weiss aber nix davon dass dieser aus dem C++ Teil weiterhin referenziert wird. D.h. du solltest im .NET Teil dafür sorgen dass der Delegate nicht collected wird. (Um den unmanaged call stub der für PInvoke erstellt wird musst du dich nicht extra kümmern, der wird am Leben gehalten so lange der Delegate lebt.)

    *: Du musst natürlich nicht, ist aber viel viel einfacher.



  • Danke, dass ist erstmal ne Menge neuer Stoff für mich - da muss ich mich erstmal einarbeiten. Aber das Prinzip habe ich denke ich verstanden. Werde dazu erstmal ein kleines Beispielprogramm erstellen und mich ggf. nochmal melden.

    Kann ich das Delegate auch als statisches Objekt definieren, so dass es nicht gelöscht wird oder dafür Speicher mit malloc reservieren um sicher zu stellen, dass sich die Adresse der Funktion nicht ändert oder diese gelöscht wird?

    Grüße,
    CJens



  • Du musst dir einfach nur im .NET Teil irgendwo eine Referenz auf den Delegate behalten, das reicht. Im einfachsten Fall, wenn du nur einen solchen Delegate brauchst, dann reicht ne statische Membervariable.

    Was du mit malloc meinst weiss ich nicht. malloc in .NET? Huch?



  • Naja, in C# meine ich:

    Marshal.AllocHGlobal
    

    Bin was C# angeht noch nicht so bewandt - aber C# verschiebt doch auch nichtstatische Variablen, oder bleibt die Adresse immer gleich?



  • Es ist ja gerade Sinn dieser Methode, daß der Speicher vom Betriebssystem (WinAPI) verwaltet wird und daher unabhängig vom GC ist.


Anmelden zum Antworten