函数的封送
在C#中调用C/C++函数时要注意:
在C/C++导出函数时,在导出函数前要加上extern关键字,不然C#中调用时会找不到。
在C/C++中,导出函数
extern __declspec(dllexport) void Add(int a, int b);
函数库名Add.dll
在C#中,需要将函数声明为:
[DllImport("Add.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern void Add(int a, int b);
函数指针在C/C++与C#中的调用
假设在C/C++中,定义了如下函数指针:
typedef int32 (*PServiceDataRead_f)(void *pContext, unsigned char *pData,
int *pDataLen, void *pArg);
要在C#中调用这个结构,则需将使用委托,重新定义:
public delegate int PServiceDataRead_f(IntPtr pContext, IntPtr pData,
IntPtr pDataLen, IntPtr pArg);
一个例子:
在C中定义了一个函数,初始化了一个结构,结构中包含一个函数指针。
//service.dll
typedef int (*PServiceDataRead_f)(int serviceId, unsigned char* pRecv);
typedef struct tagService_t
{
int serviceId;
PServiceDataRead_f PServiceDataReadFun;
}Service_t
//export funtion
export __declspec(dllexport) Service_t* InitService(); //return Service_t pointer.
InitService函数会初始化一个service结构体的指针出来,service_t结构体包含了一个int变量与函数指针PServiceDataRead_f。
当需要在C#中引用这个Service_t结构时,需要如下定义:
public class Service
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] // 调用约定
public delegate int PServiceDataRead_f(int serviceId, Byte[] pRecv);
public struct Service_t
{
int serviceId;
IntPtr PServiceDataReadFun;
}
public static PServiceDataRead_f PServiceDataReadFun;
[DllImport("service.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr InitService();
// Call
static int Main()
{
IntPtr Service = InitService(); // 指针无法直接在C#中使用,需要先将其转到IntPtr中
Byte[] recvData = new Byte[1024];
Service_t service = new Service_t(); // 初始化结构体
//将Service 转换成Service_t结构体
Marshal.PtrToStructure(Service, service);
PServiceDataReadFun
= (PServiceDataRead_f)Marshal.GetDelegateForFunctionPointer(service.PServiceDataReadFun, typeof(PServiceDataRead_f));
//指针在C#中并不能直接使用,一般得配合unsafe关键字一起使用。
/* 使用指针可以直接转换将Service 转换成指针后使用函数。
unsafe
{
Service_t *pService = (Service_t*)Service.ToPointer();
PServiceDataReadFun
= (PServiceDataRead_f)Marshal.GetDelegateForFunctionPointer(pService->PServiceDataReadFun, typeof(PServiceDataRead_f));
}
*/
int ret = PServiceDataReadFun(service.serviceId, recvData);
if(ret < 0)
{
Console.WriteLine("PServiceDataReadFun error, ret {0}", ret);
}
return 0;
}
}
第二个例子:
第一个例子是在C#中调用C/C++函数,而第二例子则是在C#中提供函数并注册到C/C++函数中。即C/C++调用C#函数。
假设有一个服务,使用C编写,但是需要在C#中进行调用,并且需要在C#中处理数据。
在C中定义了如下:
//ais.dll
typedef struct tagAisgwMsg_t
{
...
}AisgwMsg_t;
typedef int (__stdcall *AisgwMessageReport)(AisgwMsg_t *pMsg); // 注意这里的调用约定__stdcall
typedef struct AisgwServiceParam
{
unsigned int net;
AisgwMessageReport messageReport;
}AisgwServiceParam_t;
extern int32_t AisgwServiceInit(AisgwServiceParam_t *pParam);
C#:
public class Ais
{
public struct AisgwMsg_t
{
...
}
public delegate int AisgwMessageRepeort(ref AisgwMsg_t pMsg);
public struct AisgwServiceParam
{
public uint net;
AisgwMessageRepeort messageReport;
}
[DllImport("ais.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 AisgwServiceInit(ref AisgwServiceParam pParam);
public static Int32 AisgwMessageReport(ref AisGateWay.AisgwMsg pMsg)
{
...
}
static int Main()
{
AisgwServiceParam aisgwServiceParam = new AisgwServiceParam();
aisgwServiceParam.net = 0; // use default
aisgwServiceParam.messageReport = AisgwMessageReport; //注册
ret = AisgwServiceInit(ref aisgwServiceParam); // 成功之后,ais服务将会调用注册函数
if(ret < 0)
{
Console.WriteLine("AisgwServiceInit fail, ret {0}", ret);
}
return 0;
}
}