如何指定从C#到C ++ / CLI

时间:2017-08-18 07:09:46

标签: c# .net c++-cli

我想将一个函数指针(或类似的)作为回调函数传递给从C ++ / CLI调用的C#类的构造函数。 C#类是一个子模块; C ++方面是主程序。我收到Visual Studio 2017报告的错误,我无法确定要使用的正确语法。 (我是一名C ++程序员,但对CLI和C#几乎没有经验。)我找到了很多关于如何设置回调的例子,但是从C#到C ++ / CLI我找不到什么信息。

有人可以告诉我正确的语法是什么,或者如果这个目标存在根本缺陷,可以用不同的方法来实现相同的目标吗?

C#代码(似乎没问题):

namespace MyNamespace
{
    public class MyCSharpClass
    {
        private Action<string> m_logger;

        public MyCSharpClass(Action<string> logger) => m_logger = logger;

        public void logSomething()
        {
            m_logger("Hello world!");
        }
    }
}

C ++ / CLI代码(错误在System :: Action的第二个gcnew行中):

#pragma once
#pragma managed

#include <vcclr.h>

class ILBridge_MyCSharpClass
{

public:

    ILBridge_MyCSharpClass(ManagedDll_MyCSharpClass* pManagedDll_MyCSharpClass)
        : m_pManagedDll_MyCSharpClass(pManagedDll_MyCSharpClass)
    {
        m_pImpl = gcnew MyCSharpClass::MyCSharpClass(
            gcnew System::Action<System::String^>^(this, &ILBridge_MyCSharpClass::log)
        );
    }

    void log(System::String^ message) const
    {
        // ...
    }
}

报告错误:

error C3698: 'System::Action<System::String ^> ^': cannot use this type as argument of 'gcnew'
note: did you mean 'System::Action<System::String ^>' (without the top-level '^')?
error C3364: 'System::Action<System::String ^>': invalid argument for delegate constructor; delegate target needs to be a pointer to a member function

如果我按照建议删除“^”,则C3698错误消失,但C3364错误仍然存​​在。

我遵循此处建议的设计模式,但不使用代码生成:http://blogs.microsoft.co.il/sasha/2008/02/16/net-to-c-bridge/

3 个答案:

答案 0 :(得分:1)

编辑:基本解决方案

C ++ CLI中的Action可以从函数(不是成员函数,但是免费或static)或从托管ref class的成员函数创建。

为了从Action调用本机成员函数,本机成员调用需要包装在托管成员函数中。

class NativeClassType;

ref class ManagedWrapper
{
    typedef void(NativeClassType::*MemberFunc)(System::String^);
    NativeClassType* nativeObject;
    MemberFunc memberFunction;

public:
    ManagedWrapper(NativeClassType* obj, MemberFunc wrappedFunction)
        : nativeObject(obj), memberFunction(wrappedFunction)
    {
        // Action that can be used in other managed classes to effectively invoke the member function from NativeClassType
        auto actionObject = gcnew System::Action<System::String^>(this, &ManagedWrapper::CallWrapped);
    }

    void CallWrapped(System::String^ msg)
    {
        // forward the call
        (nativeObject->*memberFunction)(msg);
    }
};

原始答案和完整示例

我玩了一下,据我所知,你需要在某些时候使用本机成员函数指针处理才能回调本机成员函数...

以下示例代码为静态函数回调提供托管(ref)类,为成员函数回调提供另一个类。本机类NativeManaged使用两个桥类来演示不同的回调。

ref class ILBridge_Logger
{
private:
    System::Action<System::String^>^ loggerCallback;

public:

    ILBridge_Logger(void (*logFn)(System::String^))
    {
        loggerCallback = gcnew System::Action<System::String^>(logFn);
    }
    ILBridge_Logger(System::Action<System::String^>^ logFn)
    {
        loggerCallback = logFn;
    }



    void Test(System::String^ msgIn)
    {
        log(msgIn);
    }

    void log(System::String^ message)
    {
        loggerCallback(message);
    }
};


template<typename CallbackObject>
ref class ILBridge_MemberLogger : public ILBridge_Logger
{
    CallbackObject* o;
    void (CallbackObject::*logFn)(System::String^);
public:

    ILBridge_MemberLogger(CallbackObject* o, void (CallbackObject::*logFn)(System::String^))
        : ILBridge_Logger(gcnew System::Action<System::String^>(this, &ILBridge_MemberLogger::logMember)), o(o), logFn(logFn)
    {
    }

    // translate from native member function call to managed
    void logMember(System::String^ message)
    {
        (o->*logFn)(message);
    }
};


class NativeManaged
{
    gcroot<ILBridge_Logger^> Impl1;
    gcroot<ILBridge_Logger^> Impl2;
public:
    NativeManaged()
    {
        Impl1 = gcnew ILBridge_Logger(gcnew System::Action<System::String^>(log1));
        Impl2 = gcnew ILBridge_MemberLogger<NativeManaged>(this, &NativeManaged::log2);
    }

    void Test(System::String^ msgIn)
    {
        Impl1->Test(msgIn);
        Impl2->Test(msgIn);
    }

    // static logger callback
    static void log1(System::String^ message)
    {
        System::Console::WriteLine(L"Static Log: {0}", message);
    }

    // member logger callback
    void log2(System::String^ message)
    {
        System::Console::WriteLine(L"Member Log: {0}", message);
    }
};


int main(array<System::String ^> ^args)
{
    NativeManaged c;
    c.Test(L"Hello World");
    return 0;
}

注意:使用我不知道的C ++ 11/14/17功能处理成员函数指针可能有更优雅的方法。

答案 1 :(得分:0)

您不能像函数指针一样使用c#委托。但是你可以制作不安全的c ++ cli方法来调用c#方法。

答案 2 :(得分:0)

作为参考,这是我最终得到的解决方案。 C#代码与OP中的相同。需要一个托管(ref)课程,正如grek40在他的回答中所建议的那样。为了通过我的托管DLL使用托管类,仍然需要原始的IL Bridge类。

#pragma once
#pragma managed

#include <vcclr.h>

class ManagedDll_MyCSharpClass;
class ILBridge_MyCSharpClass;

ref class Managed_MyCSharpClass
{
    ILBridge_MyCSharpClass* m_pILBridge_MyCSharpClass;
    void (ILBridge_MyCSharpClass::*m_logger)(System::String^);
    MyCSharpClass::MyCSharpClass^ m_pImpl;

public:
    Managed_MyCSharpClass(ILBridge_MyCSharpClass* pILBridge_MyCSharpClass, void (ILBridge_MyCSharpClass::*logger)(System::String^))
        : m_pILBridge_MyCSharpClass(pILBridge_MyCSharpClass)
        , m_logger(logger)
    {
        m_pImpl = gcnew MyNamespace::MyCSharpClass(
            gcnew System::Action<System::String^>(this, &Managed_MyCSharpClass::log)
        );
    }

    void log(System::String^ message)
    {
        (m_pILBridge_MyCSharpClass->*m_logger)(message);
    }
};

class ILBridge_MyCSharpClass
{
public:

    ILBridge_MyCSharpClass(ManagedDll_MyCSharpClass* pManagedDll_MyCSharpClass)
        : m_pManagedDll_MyCSharpClass(pManagedDll_MyCSharpClass)
    {
        m_pManaged_MyCSharpClass = gcnew Managed_MyCSharpClass(this, &ILBridge_MyCSharpClass::log);
    }

    void log(System::String^ message)
    {
        // ...
    }

private:
    ManagedDll_MyCSharpClass* m_pManagedDll_MyCSharpClass;
    gcroot<Managed_MyCSharpClass^> m_pManaged_MyCSharpClass;
};