很多的Dll都是C和C++寫的,那麼如果C#想要調用Dll中的函數怎麼辦,尤其是Dll函數其中一個參數是函數指針的,即裡面有回掉函數的用C#怎麼實作?
C中的回掉函數在C#中有中特殊的處理方式叫委托,即要實作的回掉函數委托給另一個和它傳回值類型以及函數參數類型、數量一樣的方法來實作。
一、建立項目Visual C++ Win32控制台應用,工程名為CcreateDll,解決方案名為Dlltest
确定—>下一步
應用程式類型選Dll—>完成
建立頭檔案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);
頭檔案有了,在CcreateDll.cpp中include頭檔案,并實作相關函數。Ccreate.cpp如下
// 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;
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如下:
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有三個參數,原型為
}
參數1為一個帶兩個int參數的傳回值為int型的函數指針,這裡聲明一個委托
public delegate int DllcallBack(int num1, intnum2);
該委托可以指向任何帶兩個int型參數且傳回值為int型的方法,這裡的CsharpCall方法可以看作是回掉函數的實作。
public static int CsharpCall(int a, int 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這三種調用方式尚不清楚。