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++函数,如:
接下来我们再定义一个委托:
注:说明一下,在给c++传入数组参数时,必须得用[marshalAs(UnmanagedType.LPArray,SizeConst=8010)]
处理一下,相当于是告诉c++,c#传入的是一个长度为8010的数组类型,如果不写这句话的话,你回调函数接收到的参数将只有一条数据。
在调用过程中发现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的数组类型,如果不写这句话的话,你回调函数接收到的参数将只有一条数据。
相关文章推荐
- C#中调用C++的dll的参数为指针类型的导出函数(包括二级指针的情况)
- C++导出函数,C++Invoke再次封装,C#调用
- C#与DLL和COM的混合编程(1)-C#调用C++写的非托管的DLL中导出的函数
- C#中调用C++的dll的参数为指针类型的导出函数(包括二级指针的情况)
- C#与DLL和COM的混合编程(1)-C#调用C++写的非托管的DLL中导出的函数
- C#中调用C++的dll的参数为指针类型的导出函数(包括二级指针的情况)
- 如何生成DLL文件并导出函数及如何在C++或C#中调用DLL中导出的函数
- C#静态调用C++Dll导出函数,并在C++Dll中回调C#函数
- C#中调用C++的dll的参数为指针类型的导出函数(包括二级指针的情况)
- C#调用C++ dll导出函数提示找不到指定模块 by:凉游浅笔深画眉 / Net7Cracker
- C#中调用C++的dll的参数为指针类型的导出函数
- c#调用c++dll共享内存需要函数
- C#调用C++动态链接库中的函数指针与函数指针结构
- C# 调用 C++ dll 函数 时传递字符串 需要注意的问题(zz)
- C#用委托调用C++写的dll里面的函数指针
- 在 C# 中动态调用 native dll 的导出函数
- 转:使用IDispatch::Invoke函数在C++中调用C#实现的托管类库方法
- C#调用C++ DLL 中定义的函数
- C++ 调用C#函数 并指定函数的调用约定
- c# 调用c++ && c++调用c# 函数