from:https://blog.csdn.net/ferrycooper/article/details/63261771
很多的 Dll 都是 C 和 C++ 寫的,那么如果 C# 想要調用 Dll 中的函數怎么辦,尤其是 Dll 函數其中一個參數是函數指針的,即里面有回掉函數的用 C# 怎么實現?
C 中的回掉函數在 C# 中有中特殊的處理方式叫委托,即要實現的回掉函數委托給另一個和它返回值類型以及函數參數類型、數量一樣的方法來實現。
一、新建項目 Visual C++ ?? Win32 控制臺應用,工程名為 CcreateDll ,解決方案名為 Dlltest
?
確定— > 下一步
?
?
應用程序類型選 Dll — > 完成
?
新建頭文件 Ccreate.h ,聲明導出函數,其中 API_DECLSPEC? int? CallPFun( addP ?callback,? int a,? int ?b) ? 第一個參數為函數指針,內容如下:
?
[cpp] ?view plain
?copy #pragma?once ?? ?? #ifndef?Ccreate_H_ ?? #define?Ccreatel_H_ ?? ?? typedef ?? int (*addP)( int ,? int );?? ?? #ifdef?_EXPORTING? ?? #define?API_DECLSPEC?extern?"C"?_declspec(dllexport)? ?? #else? ?? #define?API_DECLSPEC??extern?"C"?_declspec(dllimport)? ?? #endif ?? ?? API_DECLSPEC?int ?Add( int ?plus1,? int ?plus2);?? API_DECLSPEC?int ?mulp( int ?plus1,? int ?plus2);?? API_DECLSPEC?int ?CallPFun(addP?callback,? int ?a,? int ?b);?? ?? #endif ??
頭文件有了,在 CcreateDll.cpp 中 include 頭文件,并實現相關函數。 Ccreate.cpp 如下
?
[cpp] ?view plain
?copy ?? ?? ?? #include?"stdafx.h" ?? #include?<iostream>? ?? #include?"Ccreate.h" ?? ?? using ? namespace ?std;?? ?? int ?Add( int ?plus1,? int ?plus2)?? {?? ????int ?add_result?=?plus1?+?plus2;?? ????return ?add_result;?? }?? int ?mulp( int ?plus1,? int ?plus2)?? {?? ????int ?add_result?=?plus1?*?plus2;?? ????return ?add_result;?? }?? ?? int ?CallPFun( int (*callback)( int ,? int ),? int ?a,? int ?b)?{?? ????return ?callback(a,?b);?? }??
函數 CallPFun 實際就是傳入函數指針及其參數,內部直接調用函數指針。
在 Release 模式下生成 CcreateDll 工程
?
生成成功后在解決方案目錄的 Release 文件夾下會看到生成的 CcreateDll.dll ,使用 Dll 查看工具可以看到三個導出函數。
?
二、新建 C# 控制臺工程 CsharpCallDll 實現調用 Dll 并使用委托實現回掉。
?
?
CsharpCallDll 工程 Program.cs 如下:
?
[csharp] ?view plain
?copy using ?System;?? using ?System.Collections.Generic;?? using ?System.Linq;?? using ?System.Text;?? using ?System.Threading.Tasks;?? using ?System.Runtime.InteropServices;?? ?? namespace ?CsharpCallDll?? {?? ????public ? class ?Program?? ????{?? ????????[UnmanagedFunctionPointer(CallingConvention.Cdecl)]?? ????????public ? delegate ? int ?DllcallBack( int ?num1,? int ?num2);?? ?? ????????[DllImport(@"../../../Release/CcreateDll.dll" ,?EntryPoint?=? "Add" ,?SetLastError?=? true ,?CharSet?=?CharSet.Ansi,?ExactSpelling?=? false ,?CallingConvention?=?CallingConvention.Cdecl)]?? ????????extern ? static ? int ?Add( int ?a,? int ?b);?? ?? ????????[DllImport(@"../../../Release/CcreateDll.dll" ,?EntryPoint?=? "mulp" ,?SetLastError?=? true ,?CharSet?=?CharSet.Ansi,?ExactSpelling?=? false ,?CallingConvention?=?CallingConvention.Cdecl)]?? ????????extern ? static ? int ?mulp( int ?a,? int ?b);?? ?? ????????[DllImport(@"../../../Release/CcreateDll.dll" ,?EntryPoint?=? "CallPFun" ,?SetLastError?=? true ,?CharSet?=?CharSet.Ansi,?ExactSpelling?=? false ,?CallingConvention?=?CallingConvention.Cdecl)]?? ????????public ? extern ? static ? int ?CallPFun(DllcallBack?pfun,? int ?a,? int ?b);?? ?????????? ????????static ? void ?Main( string []?args)?? ????????{?? ????????????int ?a?=?3;?? ????????????int ?b?=?4;?? ????????????int ?result;?? ????????????DllcallBack?mycall;?? ????????????mycall?=?new ?DllcallBack(Program.CsharpCall);?? ????????????result?=?Add(a,?b);?? ????????????Console.WriteLine("Add?返回{0}" ,?result);?? ????????????result?=?mulp(a,?b);?? ????????????Console.WriteLine("mulp?返回{0}" ,?result);?? ????????????result?=?CallPFun(mycall,?a,?b);?? ????????????Console.WriteLine("dll回掉?返回{0}" ,?result);?? ????????????Console.ReadLine();?? ????????}?? ?? ????????public ? static ? int ?CsharpCall( int ?a,? int ?b)?? ????????{?? ????????????return ?a?*?a?+?b?*?b;?? ????????}?? ????}?? }??
????
通過 DllImport 導入相應的 Dll 并聲明 Dll 中的導出函數, CcreateDll.dll 中導出函數 CallPFun 有三個參數,原型為
?
[cpp] ?view plain
?copy int ?CallPFun( int (*callback)( int ,? int ),? int ?a,? int ?b)?{?? ????return ?callback(a,?b);?? ???}??
???
參數 1 為一個帶兩個 int 參數的返回值為 int 型的函數指針,這里聲明一個委托
public? delegate? int? DllcallBack ( int ?num1,? int num2);
該委托可以指向任何帶兩個 int 型參數且返回值為 int 型的方法,這里的 CsharpCall 方法可以看作是回掉函數的實現。
?
[csharp] ?view plain
?copy public ? static ? int ?CsharpCall( int ?a,? int ?b)?? ???{?? ????????????return ?a?*?a?+?b?*?b;?? ???}??
???
通過 ?? ?????? 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." 即默認情況下, C 和 C++ 使用的 Cdecl 調用 ,但編組使用 StdCall 調用匹配的 Windows API ,對于 FastCall 、 ThisCall 、 Winapi 這三種調用方式尚不清楚。
這里將 CallingConvention= CallingConvention.Cdecl 改成 CallingConvention = CallingConvention.StdCall ,重新運行導致堆棧不對稱如下
?
總結
以上是生活随笔 為你收集整理的C#委托实现C++ Dll中的回调函数 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。