Search code examples
c#c++.netdllclr

How to call a delegate in C# (Windows forms) from an unmanaged C++ dll


I've seen a few examples of how to do this from C++ an unmanaged dll, to a C++ managed CLR dll. I currently have a C++ dll (NetPcapWrap), that references a C dll (npcap.dll). Since the npcap.dll is written in C I cannot compile with CLR so NetPcapWrap is unmanaged. My .Net Forms application references the NetPcapWrap.dll using pInvoke to gets information from the npcap.dll.

Within the npcap.dll is a looping function that writes out data to std::out what I need to do instead call a delegate of the .Net windows Forms app.

I've seen one potential solution https://www.codeproject.com/tips/695387/calling-csharp-net-methods-from-unmanaged-c-cplusp but this would require yet another dll that is compiled with CLR enabled.

Is there a way to do "reverse" pInvoke from an unmanaged C++ dll to a .Net Winforms application?

Sorry, I don't have any code to post because I can't figure out how to write the potion. I can share how I pInvoke the C++ dll but that is not my question.

Thanks


Solution

  • you need the C# code to send a delegate to a C function that expects a function pointer, then store it in some global.

    and C# needs to also store this delegate in a static variable to prevent the GC from cleaning it.

    on C/C++ side.

    // C++ file
    
    using myfunctype = void (*)(void);
    // replace with typedef in C
    // typedef void (*myfunctype)(void);
    
    extern "C" 
    __declspec( dllexport )
    myfunctype func1;
    myfunctype func1 = 0;
    
    extern "C"
    __declspec( dllexport )
    int SetFunction(myfunctype func_ptr)
    {
        func1 = func_ptr;
        return 5;
    }
    
    extern "C"
    __declspec( dllexport )
    int CallFunction()
    {
        func1();
        return 5;
    }
    

    then on C# side

    // csharp file
    using System.Runtime.InteropServices;
    
    namespace myspace;
    
    public delegate void CFunc(); 
    
    public class NativeLib
    {
        public static void CsharpFunc()
        {
            Console.WriteLine("hello world from Csharp!");
        }
        public static CFunc func_static = CsharpFunc;
    
        [DllImport("Clib", CallingConvention = CallingConvention.Cdecl)]
        public static extern int SetFunction(CFunc func);
    
        [DllImport("Clib", CallingConvention = CallingConvention.Cdecl)]
        public static extern int CallFunction();
    
    }
    

    then your main app needs to call them.

    // csharp calling application
    using myspace;
    
    class Program
    {
        public static int Main(String[] args)
        {
            NativeLib.SetFunction(NativeLib.func_static);
    
            NativeLib.CallFunction();
            return 0;
        }
    }
    

    as a bonus, this also works on linux/macos where managed C++ dlls don't exist. (tested with dotnet 6 and 8)

    while this small example works, you may need to pin any involved C# object from moving using GCHandle.

    note that you cannot allow C++ exceptions to leak into C#, you also cannot allow C# exceptions to leak into C++.