在网上有很多资料,说的是C#使用C++动态库的全局函数实现C++回调C#的实现;但本文主要是介绍在托管C++的原C++类中回调C#的实现
先讲一下全局函数的实现,下面再讲托管类的成员函数的实现;
第一步:随便创建一个C++的MFC的动态库Test,刚开始里面只是系统生成的一些代码,这里忽略;
第二步:然后我们再里面定义一个函数指针的类型,代码如下,这里只是示意方法步骤,参数列表和返回类型可以自己设置;
typedef void ( WINAPI *callback)();
第三步:随便定义一个导出函数;
void WINAPI callFunc(callback fn)
{
AfxMessageBox(_T("这是C++ 的动态库 导出函数"));
fn();
}
第四步:将C++的函数导出,代码如下;
extern "C" __declspec(dllexport) void WINAPI callFunc(callback fn);
如此,C++动态库的部份就完成,编译生成Test.dll文件即可;
第五步:在当前解决方案中,添加一个C#的WPF应用程序;
第六步:在C#中定义一个委托,要求与C++下的回调函数类型一致,代码如下
public delegate void callback();
第七步:在C#中声明外部的C++导出方法,参照前面第三步的函数样式
[DllImport("Test.dll")] static extern void callFunc(callback fn);
第八步:实现第六步声明的委托所代表的回调函数,代码如下
public static void callbackImpl()
{
MessageBox.Show("回调到了C#");
}
第九步:在C# WPF界面添加一个按钮,并添加按钮的单击事件,在单击事件中调用C++的导出函数,代码如下
private void TestDynamicC_Click(object sender, RoutedEventArgs e)
{
callFunc(new callback(callbackImpl));
}
如此便可先调用C++的导出函数,导出函数再回调C#的callbackImpl函数了;
C#调用非托管代码有四种实现方式:
1. COM调用;
2. Dllimport方法调用(直接调用从DLL导出的函数)
3. 加载托管了的非托管动态连接库
4. 直接执行机器码
以上都正常的回调实现方式(C++全局接口函数),可是如果C++的动态库是封装的类,且C++类再以托管类的方式包装成托管代码,以供C#直接使用托管类,如要在托管类实现C#传入回调函数到C++,C++再回调C#,使用方法3托管的方式便不可实现了(这个问题困扰了我三天了),到最后才想了用间接的方式实现托管的回调,上面所讲的实现方式为第二种Dllimport, 具体过实现过程如下所示;
第一步:按照以上方法,创建C++动态库,定义函数指针和导出函数
typedef void ( WINAPI *callback)();
extern "C" __declspec(dllexport) void WINAPI callFunc(callback fn);
void WINAPI callFunc(callback fn)
{
AfxMessageBox(_T("C++ 的动态库"));
fn();
}
第二步:定义一个函数指针callback类型的静态变量
static callback oTestCall = NULL;
并将第一步的fn();替换成oTestCall = fn;即导出函数的参数赋值给静态的函数指针变量;
第三步:在C++的动态库项目中定义C++的托管类,代码如下,此类可以在C#项目中当做正常的托管类使用,另外,在此类中,可以直接定义C++的类的对象(这才是托管C++类的终极目的),再用C++的对象,包装新的托管接口调用C++对象的成员函数,这就实现了C++类到C#类的托管,将MFC项目的属性->配置性性->常规->公共语言运行时支持设置为公代语言运行时支持(/clr),再编译生成Test.dll动态库文件,具体托管时C#与C++的类型对应表,请各位读者再去查找其它资料,这个网上有很多的,
#pragma once
using namespace System;
using namespace System::Collections::Generic;
using namespace System::Runtime::InteropServices;
namespace TC_Interface
{
public ref class CTableCtrl
{
public:
void TestCallBack()
{
AfxMessageBox(_T("托管C++的方式"));
oTestCall();
}
};
}
第四步:在C#中做以下相关的处理
1. 在解决方案资源管理器中展开C#项目,右键单击引用,添加引用-》前面创建的C++的动态库项目,添加完成后,在引用项目下面会列出项目引用的资源,其中就有Test,展开后可看到里面的细节;
//2. 定义委托
public delegate void callback();
//3. 声明C++的外部方法
[DllImport("Test.dll")] static extern void callFunc(callback fn);
//4. 实现回调函数
public static void callbackImpl()
{
MessageBox.Show("回调到了C#");
}
第五步:在C#中定义委托变量和C++托管类的变量
callback oCallBack;
CTableCtrl oTable = new CTableCtrl();
第六步:在C#中随便定义一个函数,将委托(回调函数)设置到C++中的全局静态函数指针,代码如下,此一步要在使用回调之前调用,
private void TestDynamicC_Click(object sender, RoutedEventArgs e)
{
oCallBack = new callback(callbackImpl);
//4. 调用动态库的C++导出函数
callFunc(oCallBack);
}
第七步:在C#中调用测试回调函数
private void TestManageC_Click(object sender, RoutedEventArgs e)
{
//在C++端的TestCallBack中调用全局静态的函数指针,即实现了C#的回调
oTable.TestCallBack();
}