http://hi.baidu.com/tease/blog/item/1fe7213802780f22b9998f5a.html
c#导出C++回调函数(cici)
最近在公司做一个新的项目,由于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++有回调函数,于是翻开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中,函数采用从右到左的压栈方式。
最后附上源代码。点击此处