0

Is it possible to call a method in c# from c++ DLL ?

I have good communication c# -> C++ , but I would like to be able to initiate a call C++ -> c# to poll some data

my code looks something like this...

host.h

/*
define the exporter for the C API
*/
#ifdef DLL_EXPORT
#define DLL_EXPORT __declspec(dllexport) 
#else
#define DLL_EXPORT __declspec(dllimport) 
#endif
class myClass
{
public:
    myCLass(){
        //do some initialising
    }
    ~myCLass(){
        //do some tidying
    }

    int getInt(int target) {
        switch (target)
        { 
        case 0:
            return someIntValue; 
        case 1:
            return someOtherIntValue;
        default:
            return 0;
        }
    }

   std::string getString(int target) {
        switch (target)
        { 
        case 0:
            return someStringValue;
        case 1:
            return someOtherStringValue;
        default:
            return "";
        }
    }
}

extern "C" {
    DLL_EXPORT  myClass* myClassConstructor();
    DLL_EXPORT void DestroySpatialser(const myClass* _pContext);
    DLL_EXPORT int getInt(myClass* _pContext, int target);
    DLL_EXPORT void getString(myClass* _pContext, int target, __out BSTR* returnStr);

}

host.cpp

extern "C" {


    myClass* myClassConstructor()
    {
        return new myClass();
    }

    void myClassDestructor(const myClass* _pContext)
    {
        if (_pContext != nullptr)
        {
            _pContext->~myClass();
            delete _pContext;
        }
    }

    //example
    int getInt(myClass* _pContext, int target)
    {
        if (_pContext == nullptr)
        {
            return K_ERR_INT;
        }
        return _pContext->getInt(target);
    }

    void getString(myClass* _pContext, int target, __out BSTR* returnStr)
    {
        std::string str;
        if (_pContext == nullptr)
        {
            str = K_ERR_CHAR;
        }
        else {
            str = _pContext->getString(target);
        }
        const std::string stdStr = str;
        _bstr_t bstrStr = stdStr.c_str();
        *returnStr = bstrStr.copy();
    }
}

csharp.cs

    private const string dllname = "myhost";

    [DllImport(dllname)]
    private static extern IntPtr myClassConstructor();

    [DllImport(dllname)]
    private static extern void myClassDestructor(IntPtr _pContext);

    [DllImport(dllname)]
    private static extern int getInt(IntPtr _pContext, int target);

    [DllImport(dllname)]
    private static extern float getFloat(IntPtr _pContext, int target);

    [DllImport(dllname, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    extern static void getString(IntPtr _pContext, int target, [MarshalAs(UnmanagedType.BStr)] out String str);

    private static IntPtr _pContext;

    public static IntPtr Create()
    {
        _pContext = myClassConstructor();
        Debug.Log("_pContextcreated");
        return _pContext;
    }

    public static void Destroy()
    {
        myClassDestructor(_pContext );
    }

    private static int _getInt(int target)
    {
        return getInt(_pContext , target);
    }

    private static string _getString(int target)
    {
        String str;
        getString(_pContext , target, out str);
        return str;
    }

This all works fine.

I'd like to add the ability to get a value from the .cs app (it will be a struct of 3 floats) which should be called from the instance of myClass.

I don't want to initiate this on c# side (i know how to do that).

Any advice?

EDIT

I implemented the solution found here https://forum.unity.com/threads/communicating-c-with-c.89930/#post-586885

with two changes (see comments below for why)

public int whatever = 0;
public delegate int myCallbackDelegate( int n, int m );
private myCallbackDelegate myCallback; // <-- add this line

[DllImport ("DLLImport_CProj")]
private static extern int TakesCallback( myCallbackDelegate fp, int n, int m );

void Awake()
{   
myCallback = new myCallbackDelegate( this.myCallback ) // <-- add this
int resp = TakesCallback(myCallback, 10, 20 ); // <-- change to this 
Debug.Log( resp );
}
garrilla
  • 539
  • 5
  • 15
  • https://stackoverflow.com/a/2168265/17034 – Hans Passant Dec 18 '18 at 13:27
  • Thanks Hans, your suggestion was similar to the solution I found at https://forum.unity.com/threads/communicating-c-with-c.89930/#post-586885 which is what I ended up using. – garrilla Dec 18 '18 at 15:38
  • Hmya, forums, the refuge and dumping ground of bad code. It has a very nasty bug, it does not take care of the `// Ensure it doesn't get garbage collected` requirement. You can't discover that bug with a simple test. – Hans Passant Dec 18 '18 at 15:45
  • thank you (again) - can you elaborate on how your linked solution does ensure that? – garrilla Dec 18 '18 at 16:16
  • It stores the delegate object in a variable so the GC can always see it. – Hans Passant Dec 18 '18 at 17:12
  • Yes. The one change I made to the bad forum code, following your method, was to create an instance of the callback as a variable. Thanks again. – garrilla Dec 18 '18 at 17:27

2 Answers2

0

Well, One way I can think of is to pass a JSON string as char array from C++ to C# and then parse it on C# and get the data. it's a communication method both languages are familiar with. Also, you will need to pass a callback from C# to C++ to allow this like explained in this question. Let me know if this helps :)

Oren_C
  • 565
  • 7
  • 22
  • Thanks for your thoughts, but I don't want to initiate this action on c# - I added a qualification to my question to be clear – garrilla Dec 18 '18 at 13:13
  • @garrilla Yes, I realized I've commented on the wrong flow. I'm editing my answer now :) – Oren_C Dec 18 '18 at 13:14
  • thanks again @Oren_c but the additional information you offered was for a managed solution. As per my question title I wanted to stay unmanaged. – garrilla Dec 18 '18 at 15:36
0

Make your C# types COMVisible, which means you will be able to call them from COM aware C++ code, e.g., using ATL.

Polyfun
  • 9,479
  • 4
  • 31
  • 39
  • thank you for your efforts, I wanted to stay unmanaged and within my existing patterns. Was interested to learn about those things though. – garrilla Dec 18 '18 at 15:35