如何指定从C#到C ++ / CLI的回调 [英] How to specify a callback from C# to C++/CLI

查看:58
本文介绍了如何指定从C#到C ++ / CLI的回调的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I would like to pass a function pointer (or similar) as a callback function to the constructor of a C# class, called from C++/CLI. The C# class is a sub-module; the C++ side is the main program. I'm getting errors reported by Visual Studio 2017, and I can't work out the correct syntax to use. (I'm a C++ programmer, but have close to zero experience with CLI and C#.) I find plenty of examples on how to set up callbacks the other way around, but from C# to C++/CLI I find little information.

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

Can somebody tell me what the correct syntax is, or show a different approach to achieve the same goal if this one is fundamentally flawed?

C#代码(看起来不错):

C# code (seems fine):

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行中:

C++/CLI code (errors are in the second gcnew line with the System::Action):

#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错误仍然存​​在。

If I remove the "^" as suggested, the C3698 error disappears but the C3364 error remains.

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

I'm following the design pattern suggested here, though not using code generation: http://blogs.microsoft.co.il/sasha/2008/02/16/net-to-c-bridge/

推荐答案

编辑:基本解决方案

C ++ CLI中的 Action 可以从一个函数(不是成员函数,而是free或 static )或托管的 ref类的成员函数中。

An Action in C++ CLI, can be created from a function (not a member function but free or static) or from the member function of a managed ref class.

要从Action调用本地成员函数,需要将本地成员调用包装在托管成员函数中。

In order to call a native member function from an Action, the native member call needs to be wrapped in a managed member function.

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);
    }
};






原始答案和完整示例

我玩了一段时间,据我所知,您需要在某个时候使用本机成员函数指针处理才能回调到本机成员函数...

I played around a little and as far as I can tell, you will need to use native member function pointer handling at some point in order to callback to native member functions...

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

The following example code provides a managed (ref) class for static function callback and another one for member function callback. The native class NativeManaged is using both bridge classes to demonstrate different callbacks.

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功能。

Note: there might be more elegant ways of handling member function pointers with the C++11/14/17 features that I'm not aware of.

这篇关于如何指定从C#到C ++ / CLI的回调的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆