不少的Dll都是C和C++寫的,那麼若是C#想要調用Dll中的函數怎麼辦,尤爲是Dll函數其中一個參數是函數指針的,即裏面有回掉函數的用C#怎麼實現?ios
C中的回掉函數在C#中有中特殊的處理方式叫委託,即要實現的回掉函數委託給另外一個和它返回值類型以及函數參數類型、數量同樣的方法來實現。api
1、新建項目Visual C++ Win32控制檯應用,工程名爲CcreateDll,解決方案名爲Dlltest函數
肯定—>下一步工具
應用程序類型選Dll—>完成spa
新建頭文件Ccreate.h,聲明導出函數,其中API_DECLSPEC int CallPFun(addP callback, inta, int b) 第一個參數爲函數指針,內容以下:指針
#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以下code
// CcreateDll.cpp : 定義 DLL 應用程序的導出函數。 // #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實際就是傳入函數指針及其參數,內部直接調用函數指針。blog
在Release模式下生成CcreateDll工程原型
生成成功後在解決方案目錄的Release文件夾下會看到生成的CcreateDll.dll,使用Dll查看工具能夠看到三個導出函數。string
2、新建C#控制檯工程CsharpCallDll實現調用Dll並使用委託實現回掉。
CsharpCallDll工程Program.cs以下:
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); //[MarshalAs(UnmanagedType.FunctionPtr)] 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有三個參數,原型爲
int CallPFun(int(*callback)(int, int), int a, int b) { return callback(a, b); }
參數1爲一個帶兩個int參數的返回值爲int型的函數指針,這裏聲明一個委託
public delegate int DllcallBack(int num1, intnum2);
該委託能夠指向任何帶兩個int型參數且返回值爲int型的方法,這裏的CsharpCall方法能夠看做是回掉函數的實現。
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,從新運行致使堆棧不對稱以下