c++ - 允许托管环境中的托管代码回调非托管代码

标签 c++ .net c++-cli clr-hosting

我有托管 clr 的 C++ 代码,以便使用用 C# 编写的 Managed.dll。

这个 .net 有一个类似于下面的方法,允许代码注册事件通知:

public void Register(IMyListener listener);

界面看起来像这样

public interface IMyListener
{
    void Notify(string details);
}

我想在程序的 C++ 部分做一些事情,由 .net 世界中的事件触发。如果有必要,我什至不介意为了使 Managed.dll 对 C++ 更友好而创建另一个托管 dll。

我在这里有哪些选择?我确信我可以实现的唯一一个是:

  • 编写另一个托管的 dll 来监听这些事件,将它们排队并让 C++ 代码通过轮询访问队列

这当然会从“中断”风格转变为“轮询”风格,具有其所有优点和缺点以及提供排队的需要。我们可以不用轮询吗?我能否以某种方式调用托管代码并为其提供一个指向 C++ 世界的函数指针作为参数?

更新 感谢 stijn 的回答和评论,我希望我朝着正确的方向前进了一点,但我想仍然存在的主要问题是如何将 fn 指针从非托管区域传递到 clr 托管环境。

假设我有一个“int fn(int)”类型的函数指针,我想传递给托管世界,这里是相关部分:

托管代码 (C++/CLI)

typedef int (__stdcall *native_fun)( int );

String^ MyListener::Register(native_fun & callback)
{
    return "MyListener::Register(native_fun callback) called callback(9): " + callback(9);
}

非托管代码

typedef int (__stdcall *native_fun)( int );
extern "C" static int __stdcall NativeFun(int i)
{
    wprintf(L"Callback arrived in native fun land: %d\n", i);
    return i * 3;
}
void callCLR()
{
    // Setup CLR hosting environment
    ...
    // prepare call into CLR
    variant_t vtEmpty;
    variant_t vtRetValue;
    variant_t vtFnPtrArg((native_fun) &NativeFun);
    SAFEARRAY *psaMethodArgs = SafeArrayCreateVector(VT_VARIANT, 0, 1);
    LONG index = 0;
    SafeArrayPutElement(psaMethodArgs, &index, &vtFnPtrArg);
    ...
    hr = spType->InvokeMember_3(bstrMethodName, static_cast<BindingFlags>(
            BindingFlags_InvokeMethod | BindingFlags_Static | BindingFlags_Public),
            NULL, vtEmpty, psaMethodArgs, &vtRetValue);
    if (FAILED(hr))
            wprintf(L"Failed to invoke function: 0x%08lx\n", hr);

spType->InvokeMember_3 调用将导致 0x80131512 结果。

我将指向 NativeFun 的指针传递给托管世界的方式或我的函数的定义方式似乎有问题。当使用 String^ 参数而不是 fn ptr 时,我可以成功调用 CLR 函数。

最佳答案

您可以在 C++/CLI 中编写一个单独的 dll 并在那里实现接口(interface),并将逻辑转发给 C++。根据我混合托管/非托管的经验,我可以说使用中间 C++/CLI 步骤是可行的方法。无需摆弄 DllImport 和功能,而是两个世界之间的坚实桥梁。它只需要一些时间来习惯语法和编码,但一旦你掌握了它,它实际上是毫不费力的。如果你需要在托管类中保存 C++ 对象,最好的方法是使用一些东西 like clr_scoped_ptr .

代码看起来像这样:

//header
#using <Managed.dll>

//forward declare some native class
class NativeCppClass;

public ref class MyListener : public IMylIstener
{
public:
  MyListener();

    //note cli classes automatically implement IDisposable,
    //which will call this destructor when disposed,
    //so used it as a normal C++ destructor and do cleanup here
  ~MyListener();

  virtual void Notify( String^ details );

private:
  clr_scoped_ptr< NativeCppClass > impl;
}

//source
#include "Header.h"
#include <NativeCppClass.h>

//here's how I marshall strings both ways
namespace
{
  inline String^ marshal( const std::string& i )
  {
    return gcnew String( i.data() );
  }

  inline std::string marshal( String^ i )
  {
    if( i == nullptr )
      return std::string();
    char* str2 = (char*) (void*) Marshal::StringToHGlobalAnsi( i );
    std::string sRet( str2 );
    Marshal::FreeHGlobal( IntPtr( str2 ) );
    return sRet;
  }
}

MyListener::MyListener() :
  impl( new NativeCppClass() )
{
}

MyListener::~MyListener()
{
}

void MyListener::Notify( String^ details )
{
  //handle event here
  impl->SomeCppFunctionTakingStdString( marshal( details ) );
}

更新 这是从托管世界调用 C++ 回调的简单解决方案:

pubic ref class CallbackWrapper
{
public:
  typedef int (*native_fun)( int );

  CallbackWrapper( native_fun fun ) : fun( fun ) {}

  void Call() { fun(); }

  CallbackWrapper^ Create( ... ) { return gcnew CallbackWrapper( ... ); }

private:
  native_fun fun;
}

如果需要,您也可以将其包装在一个 Action 中。 另一种方法是使用 GetDelegateForFunctionPointer,例如 as in this SO question

关于c++ - 允许托管环境中的托管代码回调非托管代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11306287/

相关文章:

c# - 我可以在同一个项目中引用一个 dll 的两个版本而不将它们放入 GAC 中吗?

c# - 为什么 "-less"在 "hello"之后而不是在它之前排序?

c++ - 将 STL 字符串从 C++/CLI 应用程序传递到 C++ DLL

visual-studio - 人们如何处理警告 C4793 : 'some_function' : function compiled as native?

.net - Windows Workflow Foundation(WF)的目的是什么?

.net - 如何在 C++ 中将 std::string 转换为 CV::String?

c++ - 语言如何在底层表示 UTF-8?

c++ - 如何使用 Material 的环境色代替灯光的全局环境色? (OpenGL 1)

c++ - pthread 中的线程在一些迭代后停止工作

c++ - C/C++(还有其他语言吗?)有条件提前返回良好代码实践