0
votes

I'm trying to marshal a delegate to a function pointer. The return type of the delegate is an array of strings while the return type of the function pointer is a char**

The code below throws Invalid managed/unmanaged type combination exception. How do I solve this?

//  unmanaged code

typedef char** (*MyFuncPtr)(void);

class __declspec(dllexport) MyUnmanagedClass{
private:
    MyFuncPtr mFunPtr
public:

    MyUnmanagedClass(MyFuncPtr funPtr)
    {
        mFunPtr = funPtr;
    }

    char* Func1()
    {
        //  callback
        mFunPtr();

        return "something";
    }
};

//  managed wrapper (CLI)

public delegate cli::array<String^>^ MyDelegate();

public class MyCliClass{
private:
    MyDelegate mDel;
public:

    MyCliClass(MyDelegate del)
    {
        mDel = del;
    }

    String^ Func2()
    {
        MyFuncPtr funPtr = static_cast<MyFuncPtr>(Marshal::GetFunctionPointerForDelegate(mDel).ToPointer());

        MyUnmanagedClass* muc = new MyUnmanagedClass(funPtr);
        char* retValPtr = muc->Func1();

        return context->marshal_as<String^>(retValPtr);
    }   
};

//  managed client (C#)
class Program
{
    static void Main( string[] args )
    {
        MyCliClass mcc = new MyCliClass(Func3);
        mcc.Func2();
    }

    static string[] Func3()
    {
        return new[] { "Some 1", "Some 2" };
    }
}
1
The function is impossible to call reliably from a C or C++ program. The caller cannot know how many elements are in the returned array and whether or not the storage for the strings and the array needs to be released. The exception merely tells you that the marshaller doesn't know either. It can't know. You'll have to re-think this.Hans Passant
In that case what other alternatives do I have to achieve this?Nimish Mistry
Set yourself a goal of avoiding GetFunctionPointerForDelegate. C++/CLI doesn't need it, because C++/CLI code can call both C++ and C# functions and access both C++ and C# objects directly. So what you need is a C++/CLI function with the right call signature so you can take a pointer to it and immediately get the right type (no casting!). Inside that function you can access C# objects, call delegates, whatever you need to do.Ben Voigt
I don't understand, is it possible for you to put up a quick sample?Nimish Mistry

1 Answers

0
votes

This is how I finally solved it...

//  unmanaged code
class __declspec(dllexport) NativeInterface
{
public:
    vector<string> NativeInterfaceFunc() = 0;
};

class __declspec(dllexport) UnmanagedClass{
private:
    NativeInterface* mNativeInterface
public:

    UnmanagedClass(NativeInterface* nativeInterface)
    {
        mNativeInterface = nativeInterface;
    }

    string UnmanagedClassFunc()
    {
        vector<string> values = mNativeInterface->NativeInterfaceFunc();

        ostringstream oss;

        copy(values.begin(), values.end(), ostream_iterator<string>(oss, ","));

        return oss.str();
    }
};

//  managed wrapper (CLI)
class NativeClass : NativeInterface
{
public:
    NativeInterface(ManagedInterface^ managedInterface)
    {
        mManagedInterface = managedInterface        
    };

    vector<string> NativeInterfaceFunc()
    {
        IEnumerable<String^> managedReturn = mManagedInterface->ManagedInterfaceFunc();

        vector<string> nativeReturn;

        marshal_context^ context = new marshal_context();

        for each(String^ manRet in managedReturn)
        {
            nativeReturn.push_back(context->marshal_as<const char*>(manRet))
        }

        delete context;

        return nativeReturn;
    };
private:
    gcroot<ManagedInterface^> mManagedInterface;
};

public ref class CliClass{
public:

    CliClass(ManagedInterface^ managedInterface)
    {
        mNativeInterface = new NativeClass(managedInterface);

        mUnmanagedClass = new UnmanagedClass(mNativeInterface);
    }

    ~CliClass()
    {
        delete mUnmanagedClass;
        delete mNativeInterface;
    }

    String^ CliFunc()
    {
        string nativeReturn = mUnmanagedClass->UnmanagedClassFunc();

        return gcnew String(nativeReturn.c_str());
    }   
private:
    UnmanagedClass mUnmanagedClass;
    NativeInterface mNativeInterface;
};

//  managed interface (C#)
interface ManagedInterface
{
    IEnumerable<string> ManagedInterfaceFunc();
}

//  managed interface implementation(C#)
public class ManagedClass : ManagedInterface
{
    public IEnumerable<string> ManagedInterfaceFunc()
    {
        return new List<string> { "Some 1", "Some 2" };
    }
}

//  managed client (C#)
class Program
{
    static void Main( string[] args )
    {
        MyCliClass mcc = new MyCliClass(new ManagedClass());

        Console.WriteLine(mcc.CliFunc());

        mcc.Dispose();
    }
}