有一个非托管C++编写的库类,未使用extern "C"编译得到Adapter.dll、Adapter.lib文件,其中有一个含纯虚函数的抽象类,该纯虚函数是一个回调函数,大致如下:
struct Data
{
unsinged short a;
char b[20];
...... // 多种类型变量
};
class PortInterface
{
public:
Data m_data;
....// 变量及一些操作
};
struct Info
{
char aa[20];
char bb[40];
......
};
struct AdaData : public Data
{
Info info;
}
class Adapter : public PortInterface
{
public:
AdaData m_data;
......//其他变量
public:
virtual ProcessData(const AdaData& data) = 0; // 纯需函数,当Adapter实例化,运行某些操作后,
// 将由外部(服务器)触发此函数
};
【需求】
在C#中实现Adapter各项功能,接收回调ProcessData(),并在一个响应函数中实现需要的代码
【处理过程】
1、派生抽象类,并为回调函数传递做准备
#define DllImport __declspec( dllimport )
#define DllExport __declspec( dllexport )
// 回调的代理
typedef void (__stdcall* ProcessDataCallback)(const AdaData& data);
class MyAdapter : public Adapter
{
public:
MyAdapter();
ProcessDataCallback CsFunc; // 申明代理
virtual void ProcessData(const AdaData& data); // 回调函数实例化
};
MyAdapter::MyAdapter()
{
}
void MyAdapter::ProcessData(const AdaData& data)
{
if (this->CsFunc != NULL)
{
this->CsFunc(data); // 将回调传递给代理
}
}
2、做全局函数,为C#调用提供静态访问接口
MyAdapter* m_adapter;
// 实例化MyAdapter类
extern "C" DllExport extern void BuildClass()
{
m_adapter = new MyuAdapter();
}
// 设置类Adapter回调函数使用的C#函数
extern "C" DllExport extern void SetCallFunc(ProcessDataCallback func)
{
if(m_adapter)
m_adapter->CsFunc = func;
}
// 删除MyAdapter类实例
extern "C" DllExport extern void DisposeClass()
{
if(m_adapter)
delete m_adapter;
}
extern "C" DllExport extern ...
...// 其他函数
为简化,上述程序都在MyAdapter.h文件中,当然必须要有MyAdapter.cpp,编译通过后得到MyAdapter.dll
3、在C#中使用回调函数:引用中添加MyAdapter
using MyAdapter;
public partial class Form1 : Form
{
// 非托管结构体的托管化
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct DataStructMana
{
UInt16 a
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
string b;
......
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
string aa;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)]
string bb;
......
};
[DllImport(@"MyAdapter.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void BuildClass();
[DllImport(@"MyAdapter.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void DisposeClass();
// 申明代理,用于向C++dll传递执行函数
[System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute
(System.Runtime.InteropServices.CallingConvention.StdCall)]
public delegate void Callback(ref DataStructMana ds);
[DllImport(@"MyAdapter.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void SetCallFunc(Callback func);
//实例化代理
public Callback cb = new Callback(OnProcessData);
public Form1()
{
InitializeComponent();
BuildClass(); //创建Adapter类实例
SetCallFunc(cb); // 向Adapter类实例的回调函数传递C#执行函数
}
public static void OnProcessData(ref DataStructMana ds)
{
try{
int i = 0; // * :此处设置断点时...
MessageBox.Show("Raise the ProcessData function");
// 添加需要进行的处理代码
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
编译上述代码,运行服务器程序引发MyAdapter类的回调函数执行,可以从C++的ProcessData()回调函数传递到C#的OnProcessData(),程序运行到弹出对话框显示“Raise the ProcessData function”后就自动关闭,没有进入异常处理。
*:在*处设置断点后,查看获取的数据ds与C++中的const AdaData& data内容一样。
无法在Release版本下进行调试,因为给的dll和lib中的类直接通过链接器设置使用,在Release下会报错。
搜索了不少网站查阅类似问题,但其解决方法不是不适用,就是无效。C#中代理的申明和实例化都放在Form1类中了,回调函数OnProcessData()的参数是引用,从C++传递过来的,不需要本地赋值,也不能设成IntPtr类型,会报错。
知道与C#托管有关,但不知道该怎么查。请各位大虾指点。
------解决思路----------------------
在“调试”选择“启用非托管代码调试”后
托管代码和非托管代码可以联合调试
可以参考这个
http://blog.csdn.net/tpriwwq/article/details/42913119