C#委托实现C++ Dll中的回调函数

17 篇文章 3 订阅

from:https://blog.csdn.net/ferrycooper/article/details/63261771

很多的Dll都是CC++写的,那么如果C#想要调用Dll中的函数怎么办,尤其是Dll函数其中一个参数是函数指针的,即里面有回掉函数的用C#怎么实现?

C中的回掉函数在C#中有中特殊的处理方式叫委托,即要实现的回掉函数委托给另一个和它返回值类型以及函数参数类型、数量一样的方法来实现。


一、新建项目Visual C++  Win32控制台应用,工程名为CcreateDll,解决方案名为Dlltest

 

确定—>下一步

 

 

应用程序类型选Dll>完成

 

新建头文件Ccreate.h,声明导出函数,其中API_DECLSPEC int CallPFun(addP callback, inta, int b) 第一个参数为函数指针,内容如下:

 

[cpp]  view plain  copy
  1. #pragma once  
  2.   
  3. #ifndef Ccreate_H_  
  4. #define Ccreatel_H_  
  5.   
  6. typedef  int(*addP)(intint);  
  7.   
  8. #ifdef _EXPORTING   
  9. #define API_DECLSPEC extern "C" _declspec(dllexport)   
  10. #else   
  11. #define API_DECLSPEC  extern "C" _declspec(dllimport)   
  12. #endif  
  13.   
  14. API_DECLSPEC int Add(int plus1, int plus2);  
  15. API_DECLSPEC int mulp(int plus1, int plus2);  
  16. API_DECLSPEC int CallPFun(addP callback, int a, int b);  
  17.   
  18. #endif  

头文件有了,在CcreateDll.cppinclude头文件,并实现相关函数。Ccreate.cpp如下

 

[cpp]  view plain  copy
  1. // CcreateDll.cpp : 定义 DLL 应用程序的导出函数。  
  2. //  
  3.   
  4. #include "stdafx.h"  
  5. #include <iostream>   
  6. #include "Ccreate.h"  
  7.   
  8. using namespace std;  
  9.   
  10. int Add(int plus1, int plus2)  
  11. {  
  12.     int add_result = plus1 + plus2;  
  13.     return add_result;  
  14. }  
  15. int mulp(int plus1, int plus2)  
  16. {  
  17.     int add_result = plus1 * plus2;  
  18.     return add_result;  
  19. }  
  20.   
  21. int CallPFun(int(*callback)(intint), int a, int b) {  
  22.     return callback(a, b);  
  23. }  

函数CallPFun实际就是传入函数指针及其参数,内部直接调用函数指针。

Release模式下生成CcreateDll工程

 

生成成功后在解决方案目录的Release文件夹下会看到生成的CcreateDll.dll,使用Dll查看工具可以看到三个导出函数。

 

二、新建C#控制台工程CsharpCallDll实现调用Dll并使用委托实现回掉。

 

 

CsharpCallDll工程Program.cs如下:

 

[csharp]  view plain  copy
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.Threading.Tasks;  
  6. using System.Runtime.InteropServices;  
  7.   
  8. namespace CsharpCallDll  
  9. {  
  10.     public class Program  
  11.     {  
  12.         [UnmanagedFunctionPointer(CallingConvention.Cdecl)]  
  13.         public delegate int DllcallBack(int num1, int num2);  
  14.   
  15.         [DllImport(@"../../../Release/CcreateDll.dll", EntryPoint = "Add", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = false, CallingConvention = CallingConvention.Cdecl)]  
  16.         extern static int Add(int a, int b);  
  17.   
  18.         [DllImport(@"../../../Release/CcreateDll.dll", EntryPoint = "mulp", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = false, CallingConvention = CallingConvention.Cdecl)]  
  19.         extern static int mulp(int a, int b);  
  20.   
  21.         [DllImport(@"../../../Release/CcreateDll.dll", EntryPoint = "CallPFun", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = false, CallingConvention = CallingConvention.Cdecl)]  
  22.         public extern static int CallPFun(DllcallBack pfun, int a, int b);  
  23.         //[MarshalAs(UnmanagedType.FunctionPtr)]  
  24.         static void Main(string[] args)  
  25.         {  
  26.             int a = 3;  
  27.             int b = 4;  
  28.             int result;  
  29.             DllcallBack mycall;  
  30.             mycall = new DllcallBack(Program.CsharpCall);  
  31.             result = Add(a, b);  
  32.             Console.WriteLine("Add 返回{0}", result);  
  33.             result = mulp(a, b);  
  34.             Console.WriteLine("mulp 返回{0}", result);  
  35.             result = CallPFun(mycall, a, b);  
  36.             Console.WriteLine("dll回掉 返回{0}", result);  
  37.             Console.ReadLine();  
  38.         }  
  39.   
  40.         public static int CsharpCall(int a, int b)  
  41.         {  
  42.             return a * a + b * b;  
  43.         }  
  44.     }  
  45. }  

    

通过DllImport导入相应的Dll并声明Dll中的导出函数,CcreateDll.dll中导出函数CallPFun有三个参数,原型为

 

[cpp]  view plain  copy
  1. int CallPFun(int(*callback)(intint), int a, int b) {  
  2.     return callback(a, b);  
  3.    }  
   

参数1为一个带两个int参数的返回值为int型的函数指针,这里声明一个委托

public delegate int DllcallBack(int num1, intnum2);

该委托可以指向任何带两个int型参数且返回值为int型的方法,这里的CsharpCall方法可以看作是回掉函数的实现。

 

[csharp]  view plain  copy
  1. public static int CsharpCall(int a, int b)  
  2.    {  
  3.             return a * a + b * b;  
  4.    }  
   

通过        DllcallBack mycall;

           mycall = new DllcallBack(Program.CsharpCall);

  把实际要完成的工作交给CsharpCall去完成。

    运行CsharpCallDll,结果如下:

  

 

是不是实现了C#委托实现回掉

 

最后还有如果声明委托时在public delegate int DllcallBack(int num1, int num2);上面没有[UnmanagedFunctionPointer(CallingConvention.Cdecl)]这一句,那么运行时将会出现System.AccessViolationException异常,如下

 

 

还有Dll调用约定,CallingConvention.有五种调用方式

CallingConvention= CallingConvention.StdCall

CallingConvention= CallingConvention.Cdecl

CallingConvention= CallingConvention.FastCall

CallingConvention= CallingConvention.ThisCall

CallingConvention= CallingConvention.Winapi

到底使用哪种方式,网上有说"Bydefault, C and C++ use cdecl - but marshalling uses stdcall to match theWindows API."即默认情况下,CC++使用的Cdecl调用,但编组使用StdCall调用匹配的Windows API,对于FastCallThisCallWinapi这三种调用方式尚不清楚。

这里将CallingConvention= CallingConvention.Cdecl改成CallingConvention = CallingConvention.StdCall,重新运行导致堆栈不对称如下

 



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值