当前位置: 代码迷 >> C# >> C#使用非托管C++dll中的回调函数,执行一次后程序自动关闭有关问题
  详细解决方案

C#使用非托管C++dll中的回调函数,执行一次后程序自动关闭有关问题

热度:580   发布时间:2016-04-28 08:38:43.0
C#使用非托管C++dll中的回调函数,执行一次后程序自动关闭问题
有一个非托管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
  相关解决方案