简体   繁体   English

将C#回调函数传递到托管和非托管C ++库中

[英]Passing a C# callback function into managed and unmanaged C++ libraries

I am looking to create a C# program that calls a managed C++ dll, which then executes functionality in a native C++ dll. 我正在寻找创建一个调用托管C ++ dll的C#程序,然后在本机C ++ dll中执行功能。 As part of this call, I want to provide a C# callback function that could be invoked by the native C++ dll. 作为此调用的一部分,我想提供一个可由本机C ++ dll调用的C#回调函数。

The C# code would call some managed C++ code, which would call the following native C++ code; C#代码将调用某些托管C ++代码,而托管C ++代码将调用以下本机C ++代码。 I want the native C++ code to call my C# callback cb : 我希望本机C ++代码调用我的C#回调cb

int dobatch(CString str)
{
    // I want to to call c#
    if (cb)
        return(cb(str)); // this would execute the function passed from c#.
}

Any ideas...I just can't seem to get the C# callback to mix with calling the native C++ dobatch() function via the managed C++ dll. 任何想法...我似乎似乎无法使C#回调与通过托管C ++ dll调用本机C ++ dobatch()函数混合使用。

This boils down to providing a C# callback function to a native C++ dll. 归结为向本地C ++ dll提供C#回调函数。 The only extra detail here is that you seem to be providing the C# callback function through an intermediate managed C++ DLL. 这里唯一的额外细节是您似乎正在通过中间托管的C ++ DLL提供C#回调函数。

I would attack this in the following tasks, adding one complication at a time: 我将在以下任务中对此进行攻击,一次添加一个复杂功能:

  • Figure out how to pass a simple C# callback directly to a native C++ dll. 弄清楚如何将简单的C#回调直接传递给本机C ++ dll。
  • Figure out how to pass that C# callback to a native C++ dll, but through a managed C++ library. 弄清楚如何通过托管C ++库将C#回调传递给本地C ++ dll。
  • Figure out how to handle the signature of the arguments that your callback needs to handle 弄清楚如何处理回调需要处理的参数签名

Part one - this answer already shows you how to pass managed C# callbacks to native C++ libraries using C# delegates: Passing a C# callback function through Interop/pinvoke 第一部分 -该答案已经向您展示了如何使用C#委托将托管的C#回调传递给本机C ++库: 通过Interop / pinvoke传递C#回调函数

Part two - passing .Net delegates from managed C++ to unmanaged C++ - can be found at the following MSDN page: How to: Marshal Callbacks and Delegates By Using C++ Interop . 从托管C ++到非托管C ++的第二部分传递.Net委托,可以在下面的MSDN页面上找到: 如何:使用C ++ Interop整理回调和委托 I've pasted the code samples below in case of link rot. 为了防止链接腐烂,我粘贴了以下代码示例。

Some discussion on part two - if your native C++ code does not store your callback function for any longer than you call your native C++ code, then your job is easier, because Garbage Collection in the managed C++ code won't get in your way. 关于第二部分的一些讨论-如果您的本机C ++代码存储回调函数的时间不长于调用本机C ++代码的时间,那么您的工作就容易了,因为托管C ++代码中的垃圾回收不会给您带来麻烦。 If it does store your callback for a longer than the duration of your call into it, then you need to inform the GC of this fact or it may collect the objects representing the callback before the last time the native C++ code calls it; 如果它确实将回调存储的时间长于调用的持续时间,则您需要将此事实通知GC,否则它可能在上一次本机C ++代码调用该回调之前收集表示该回调的对象; if that were to happen, the native C++ code would using memory that's been freed, causing crashes. 如果发生这种情况,本机C ++代码将使用已释放的内存,从而导致崩溃。

Part three - dealing with the signature of the callback. 第三部分 -处理回调的签名。

From your answer, we can see that you'd like to pass a CString back to C#. 从您的答案中,我们可以看到您想将CString传递回C#。 Unfortunately, CString is not a portable, standard type, and its internal structure depends on the C++ runtime the native dll was compiled with; 不幸的是, CString不是可移植的标准类型,它的内部结构取决于编译本机dll的C ++运行时。 it also likely depends on how the compiler decided to assemble that type too, which means that CString 's structure is probably arbitrary depending on who's providing the code. 它还可能取决于编译器也决定如何汇编该类型,这意味着CString的结构可能是任意的,具体取决于谁提供代码。 Refer to the following answer for more information about this: PInvoke with a CString . 有关此的更多信息,请参考以下答案: 用CString调用PInvoke

However, you may have an easy out if you can change the native C++ library - change how your native function invokes your managed callback, so that the managed callback is passed a char* , something like the following: 但是,如果可以更改本机C ++库,则可能会很容易-更改本机函数调用托管回调的方式,以便将托管回调传递给char* ,如下所示:

int dobatch(CString str)
{
    // I want to to call c#
    if (cb) 
    {
        // Invoke the 'CString::operator LPCSTR' operator
        // Note that 'LPCSTR' is define for 'char*'. 
        // See: https://msdn.microsoft.com/en-us/library/aa300569%28v=vs.60%29.aspx
        char* strCharPtr = (char*)str;
        return( cb(strCharPtr) );
    }
}

In case of link rot, here are the code samples from the MSDN page on passing .Net delegates from managed C++ to unmanaged C++: 如果出现链接腐烂,请参见MSDN页面上的代码示例,这些代码示例是将.Net委托从托管C ++传递到非托管C ++的:

Listing one - the native C++ library does not store the callback any longer than the native C++ is being called: 清单一-本机C ++库存储回调的时间不超过调用本机C ++的时间:

// MarshalDelegate1.cpp
// compile with: /clr
#include <iostream>

using namespace System;
using namespace System::Runtime::InteropServices;

#pragma unmanaged

// Declare an unmanaged function type that takes two int arguments
// Note the use of __stdcall for compatibility with managed code
typedef int (__stdcall *ANSWERCB)(int, int);

int TakesCallback(ANSWERCB fp, int n, int m) {
   printf_s("[unmanaged] got callback address, calling it...\n");
   return fp(n, m);
}

#pragma managed

public delegate int GetTheAnswerDelegate(int, int);

int GetNumber(int n, int m) {
   Console::WriteLine("[managed] callback!");
   return n + m;
}

int main() {
   GetTheAnswerDelegate^ fp = gcnew GetTheAnswerDelegate(GetNumber);
   GCHandle gch = GCHandle::Alloc(fp);
   IntPtr ip = Marshal::GetFunctionPointerForDelegate(fp);
   ANSWERCB cb = static_cast<ANSWERCB>(ip.ToPointer());
   Console::WriteLine("[managed] sending delegate as callback...");

// force garbage collection cycle to prove
// that the delegate doesn't get disposed
   GC::Collect();

   int answer = TakesCallback(cb, 243, 257);

// release reference to delegate
   gch.Free();
}

Listing two - the native C++ stores the callback, and thus, we have to inform the GC of this fact: 清单2-本机C ++存储回调,因此,我们必须将此事实告知GC:

// MarshalDelegate2.cpp
// compile with: /clr 
#include <iostream>

using namespace System;
using namespace System::Runtime::InteropServices;

#pragma unmanaged

// Declare an unmanaged function type that takes two int arguments
// Note the use of __stdcall for compatibility with managed code
typedef int (__stdcall *ANSWERCB)(int, int);
static ANSWERCB cb;

int TakesCallback(ANSWERCB fp, int n, int m) {
   cb = fp;
   if (cb) {
      printf_s("[unmanaged] got callback address (%d), calling it...\n", cb);
      return cb(n, m);
   }
   printf_s("[unmanaged] unregistering callback");
   return 0;
}

#pragma managed

public delegate int GetTheAnswerDelegate(int, int);

int GetNumber(int n, int m) {
   Console::WriteLine("[managed] callback!");
   static int x = 0;
   ++x;

   return n + m + x;
}

static GCHandle gch;

int main() {
   GetTheAnswerDelegate^ fp = gcnew GetTheAnswerDelegate(GetNumber);

   gch = GCHandle::Alloc(fp);

   IntPtr ip = Marshal::GetFunctionPointerForDelegate(fp);
   ANSWERCB cb = static_cast<ANSWERCB>(ip.ToPointer());
   Console::WriteLine("[managed] sending delegate as callback...");

   int answer = TakesCallback(cb, 243, 257);

   // possibly much later (in another function)...

   Console::WriteLine("[managed] releasing callback mechanisms...");
   TakesCallback(0, 243, 257);
   gch.Free();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM