从C ++ DLL(使用“Dllimport”从C#加载)调用C#方法/函数

在单个标题中恢复它有点困难,所以在这里我的情况。

我正在构建一个加载C ++库的C#应用​​程序。
我从该C ++ DLL调用函数。
但我也喜欢我的C ++ DLL从C#应用程序调用函数(即导入/运行它)…

这里有一段代码使它更全面:

// I'm importing functions from my native code [DllImport(dllFilePath, CallingConvention = CallingConvention.Cdecl)] public static extern int returnIntValue(int value); [DllImport(dllFilePath, CallingConvention = CallingConvention.Cdecl)] public static extern int returnIntArrValueAt(int[] values, int index); // Here is the kind of code it should be // [DllImport(dllFilePath, CallingConvention = CallingConvention.Cdecl)] // public static extern void callCSharpFunction( FunctionPointer fctPointer ); main { // I run the function int intValue1 = MyAddIn.returnIntValue(147852369); int intValue2 = MyAddIn.returnIntArrValueAt( new int[] { 9, 4, 3, 2, 1, 0 }, 5); // Here is an example function I use to call my C# func from C++ // MyAddIn.returnIntValue( &myCSharpFunction ); } // This is the method I'd like to call from my C++ imported library static public void myCSharpFunction() { MessageBox.Show("Called from C++ code !!"); } 

所以,要恢复:

  • C#代码导入我的C ++ DLL
  • C#运行C ++ DLL中的函数
  • C ++ DLL方法调用一个C#方法,显示一个MessageBox(即)

这是我对这个问题的回答, 这个问题很相似。 我的示例不使用回调参数。 但是,由于你的参数是整数,所以你不应该有任何问题。

基本上, Marshal.GetFunctionPointerForDelegate方法从委托创建IntPtr 。 该委托应该与C ++库中使用的函数指针具有相同的签名。

 // This is the delegate type that we use to marshal // a function pointer for C to use. You usually need // to specify the calling convention so it matches the // convention of your C library. The signature of this // delegate must match the signature of the C function // pointer we want to use. [UnmanagedFunctionPointer(CallingConvention.Cdecl)] delegate void FunctionPointer(); // This is the function we import from the C library. [DllImport(dllFilePath)] static extern void callCSharpFunction(IntPtr fctPointer); // This is the placeholder for the function pointer // we create using Marshal.GetFunctionPointerForDelegate IntPtr FctPtr; // This is the instance of the delegate to which our function // pointer will point. FunctionPointer MyFunctionPointer; // This calls the specified delegate using the C library. void CallFunctionPointer(FunctionPointer cb) { // make sure the delegate isn't null if (null == cb) throw new ArgumentNullException("cb"); // set our delegate place holder equal to the specified delegate MyFunctionPointer = cb; // Get a pointer to the delegate that can be passed to the C library FctPtr = Marshal.GetFunctionPointerForDelegate(MyFunctionPointer ); // call the imported function with that function pointer. callCSharpFunction(FctPtr); } 

这可以做到,而且总体而言不是那么大的交易。 但是有几点需要考虑。

  • 既然您说的是C ++而不是C,请注意除了静态类,实例方法,友元函数等之外,还有一些函数由于名称重整而无法通过DllImport加载。 避免使用COM,在C语言中包装C ++有时会被用作一种更便携的策略,允许库被其他语言包装.Net或其他语言。 类似地,这些相同的注意事项中的一些适用于包装库中的回调,就像您的情况一样。 虽然你的DLL中的原型似乎不是一个问题,但如果它真的是用C ++编译器构建的,那么可能值得查看导出的符号名称以确保它们。

  • 告诉您搜索的帮助,您需要知道词汇。 通常,当函数将在函数调用期间或稍后调用的另一个函数作为参数时,将其称为回调 。 在C和C ++的世界中,这是基于一种称为函数指针的语言特性(它也用于回调以外的目的)。 通常在MS文档中,让不同函数在运行时动态绑定到不同调用者的整个过程称为委托 。 函数指针的C#等价物是称为使用C#关键字委托创建的委托的对象 。 我建议首先使用此function在C#中创建一些实验程序,以便首先了解其工作原理。

  • 此外,DllImport是实现的一部分,您使用的实际.Net工具是pinvoke 。 这也应该在寻找更多信息时。 你想要做的是通过将它作为函数指针编组来导出你的代理与pinvoke。 也就是说,.Net将为本机代码创建一个shim函数来运行。 通常需要多次尝试的两个大问题区域是:1)确保此填充函数/封送委托本身具有正确的调用约定 ,以及2)此幕后填充函数的对象生存期是这样的,即它仍然存在时本机DLL将使用它。 这个曲线球有时可能需要手动覆盖垃圾收集行为,但通常这意味着只保留对它的引用。

  • 我实际上认为Mono文档在这方面也比MSDN好。

好吧,经过一些开始回零的测试后,我终于设法让这个回调运行了!

所以这是我为使用回调而创建的测试项目。

在C ++方面

export.def

 LIBRARY TestCallBack EXPORTS callCSharpFunction 

TestCallBack.cpp

 __declspec(dllexport) void callCSharpFunction ( void *fctPointer(int) ) { fctPointer(123456); } 

这个C ++项目被构建为“DLL”文件,并放在C#项目文件夹中的“lib”项目中。

在C#方面

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; namespace ConsoleApplication1 { class Program { // Set the library path const string dllFilePath = "C:\\PathToProject\\TestCallBack\\ConsoleApp\\lib\\TestCallBack.dll"; // This is the delegate type that we use to marshal // a function pointer for C to use. You usually need // to specify the calling convention so it matches the // convention of your C library. The signature of this // delegate must match the signature of the C function // pointer we want to use. [UnmanagedFunctionPointer(CallingConvention.Cdecl)] delegate void FunctionPointer( int nb); // This is the function we import from the C library. //[DllImport(dllFilePath)] [DllImport(dllFilePath, CallingConvention = CallingConvention.Cdecl)] public static extern void callCSharpFunction(IntPtr fctPointer); // This is the placeholder for the function pointer // we create using Marshal.GetFunctionPointerForDelegate static IntPtr FctPtr; // This is the instance of the delegate to which our function // pointer will point. FunctionPointer MyFunctionPointer; // This calls the specified delegate using the C library. void CallFunctionPointer(FunctionPointer cb) { // make sure the delegate isn't null if (null == cb) throw new ArgumentNullException("cb"); // set our delegate place holder equal to the specified delegate MyFunctionPointer = cb; // Get a pointer to the delegate that can be passed to the C lib FctPtr = Marshal.GetFunctionPointerForDelegate(MyFunctionPointer); // call the imported function with that function pointer. callCSharpFunction(FctPtr); } static void Main(string[] args) { // This is the instance of the delegate to which our function // pointer will point. FunctionPointer printInConsoleDelegate; // Create the delegate object "MyFunctionPointer" that references printInConsoleDelegate = new FunctionPointer(printInConsole); // Get a pointer to the delegate that can be passed to the C lib IntPtr printInConsolePtr = Marshal.GetFunctionPointerForDelegate(printInConsoleDelegate); Console.WriteLine( "Call C++ which's calling back C# func \"printInConsole\""); // Call C++ which calls C# callCSharpFunction(printInConsolePtr); // Stop the console until user's pressing Enter Console.ReadLine(); } public static void printInConsole(int nb) { // Write the parameter in the console Console.WriteLine("value = " + nb + "; "); } } } 

Unmanaged Exports项目可能会帮助您:
https://sites.google.com/site/robertgiesecke/Home/uploads/unmanagedexports

基本上,它允许您使用DllExport属性从程序集中导出函数。 然后可以像普通的本机Dll导出一样使用这些函数。 它类似于DllImport属性的缺失对应物。

然后你会像这样声明你的方法:

 [DllExport("myCSharpFunction")] static public void myCSharpFunction() { MessageBox.Show("Called from C++ code !!"); } 

但是这种双向依赖通常对我来说是可疑的。 也许在你的情况下也可以使用回调,就像ken建议的那样。