您的位置:首页 > 编程语言 > C语言/C++

C# 调用C++的导出函数,含有回掉函数

2016-11-08 10:17 405 查看
最近在公司做一个新的项目,由于C#做界面是非常快速的,于是决定用C#重写C++的界面,而原来的C++动态链接库是不需要修改的。只需要调用一下就可以了。
在调用过程中发现C++有回调函数,于是翻开MSDN终于发现,回调函数是使用委托来调用。
比如:
在C++中的回调函数是这样定义的:
typedef void (*CiCiCallBack) (bool started, void* client,char *message);

导出函数这样定义:
extern "C" _declspec(dllexport) bool Test(char* fileName, CiCiCallBack callback)
{
int n =10000;
MessageBoxExA(NULL,fileName,NULL,NULL,NULL);
callback(TRUE,&n, strcat(fileName, " hello world"));
return TRUE;
}

于是在C#中可以这样写:
public delegate void CiCiCallBack(bool started, IntPtr client,string msg);

[DllImport("CppCallBackDll.dll", EntryPoint = "Test", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Winapi)]
public static extern bool Test(string fileName, [In] CiCiCallBack callback);

在调用的时候这样调用:
CiCiCallBack callbackDelegate = new CiCiCallBack (CallBack);
Test(Application.ExecutablePath, callbackDelegate);

当然还得定义一个回调函数:
private void CallBack(bool flag, IntPtr client, string msg)
{
MessageBox.Show(msg, "", MessageBoxButtons.OK, MessageBoxIcon.Information);
}

理论上当执行到Test(Application.ExecutablePath, callbackDelegate);时候会自动调用回调函数。
但是实际却报出了这个错误:
Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention
with a function pointer declared with a different calling convention.

熟悉C++ 编程的人应该知道这个错误一般是由以下两种情况产生的:
1、参数传递有错误,尤其是指针传递的时候。
2、函数部分地方的约定不统一(压栈方式可能不统一)。
本文就是第二个原因,不难发现CPP中回调函数的约定和导出函数的约定是不统一的。于是强制在CPP代码中将回调函数定义为:
typedef void (_stdcall *CiCiCallBack) (bool started, void* client,char *message);
将导出函数修改为:
extern "C" _declspec(dllexport) bool _stdcall Test(char* fileName, CiCiCallBack callback)

对了,就是加了一个_stdcall得约定条件。_stdcall是Pascal程序的缺省调用方式,通常用于Win32 Api中,函数采用从右到左的压栈方式。

在c#中调用C++函数,如:

[DllImport("Test.dll",ChartSet.Ansi,EntryPoint="ReadMyVideo",ExactSpelling=false,CallingConvertion=CallingConvertion.Cdecl)]

private static extern void Test(string fileName,CallbackDelegate callback);


接下来我们再定义一个委托: 

public delegate void CallbackDelegate([marshalAs(UnmanagedType.LPArray,SizeConst=8010)]byte[] buffer,int count);

public static CallbackDelegate callback;


注:说明一下,在给c++传入数组参数时,必须得用[marshalAs(UnmanagedType.LPArray,SizeConst=8010)]
处理一下,相当于是告诉c++,c#传入的是一个长度为8010的数组类型,如果不写这句话的话,你回调函数接收到的参数将只有一条数据。 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息