今昔可比流行C#与C++融合:C#做GUI,开垦功能高,C++做运算,运营功能高,二者兼得。

C++通过Callback向C#传递数据,

前些天比较流行C#与C++融合:C#做GUI,开拓成效高,C++做运算,运维功能高,二者兼得。

但是C++与C#必然存在数量交互,C#与C++dll的多寡交互向来都以贰个令人高烧的主题材料。

从调用情势看也会有二种境况:

1、C#调用C++函数

这种情景用的可比多,数据流向能够是C#流向C++,通过参数将数据传递给C++(如:SetData(double[]
data));也能够是C++流向C#(如:GetData(double[] data))。

2、C++ Callback

这种场所是C++中经过Callback的格局调用C#代码,类似于C++做过一些管理后向C#发送事件,事件能够辅导数量(如管理后的多寡)。则C++中定义函数指针的措施是:

typedef  void(*Render)(double* data, BOOL* color);

 

C#用作委托,定义的函数被C++ callback:

public delegate void RenderCallback([MarshalAs(UnmanagedType.LPArray,
SizeConst =23)]double[] data, [MarshalAs(UnmanagedType.LPArray,
SizeConst = 23)]int[] colors);

相对注意,delegate中的double[]数组一定要增加马尔斯halAs标志,标志为传送数组,而且必须钦赐传递的数码,假设不标志数量,则每一遍只传递一个数值,那几个题材折磨作者很久才解决!

其余注意事项:

1、如何在C#中保险C++的函数指针

回调函数的另三个注意事项是向C++ dll传递回调函数指针的难题

若是有个函数向C++dll传递指针:

public delegate void EKFRenderCallback(string data, string colors);

public class EKFLib
{
    [DllImport("EKFLib.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    public static extern void SetRenderCallback(EKFRenderCallback render);

  

C#中如下传递被回调的函数:

public void RenderCallback(string data, string color)
{
    // rendering
}

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    EKFLib.SetRenderCallback(RenderCallback);
    EKFLib.Init();
}

  

那即便没什么难点,不过经过SetRenderCallback()传入到C++的指针不受托管代码管理,在C#中认为此指针对象未被其它轮代理公司码引用,GC做垃圾回收时,将会把C#本土的空指针回收,导致C++十分的小概奉行回调,出现“CallbackOnCollectedDelegate”错误:

对“MotionCapture!MotionCapture.EKFRenderCallback::Invoke”类型的已垃圾回收委托开始展览了回调。那只怕会形成应用程序崩溃、损坏和数据丢失。向非托管代码传递委托时,托管应用程序必须让那几个委托儿和保育持活动状态,直到确信不会再也调用它们。

微软官方网址的事例是调节GC回收机制,那是个相比较愚昧的点子,尤其自然的主意是把信托定义成二个属性,指向二个new出来的callback,然后再把那么些callback传递进C++dll中,那样,在C#端有对象引用,保险了GC不会回收此callback:

public void RenderCallback(string data, string color)
{
    // rendering
}

private EKFRenderCallback render;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
    render = new EKFRenderCallback(RenderCallback);
    EKFLib.SetRenderCallback(render);
    EKFLib.Init();
}

2、__stdcall与_cdecl传递数据

目前一个门类是通过C++ 的
dll做高速运算,然后把结果数据经过Callback的法门回调给C#(分界面部分),结果接二连三在C#中接受回调事件后就平素挂掉(程序直接在并非提醒的情况下退出,未有另向外调运试新闻或许升迁)。

导致难点的缘由是,私下认可情状下,C++中如下概念的函数指针,暗中同意是以_cdecl格局调用的:

typedef  void(*Render)(double* data, BOOL* color);

这种气象下,参数宾馆是由调用者(C++一侧)维护的,在C++调用此回调函数后,会把参数弹出宾馆而自由,导致C#读取数据时出现莫明其妙的失实。

以上是回调函数字传送递数组只怕现身的景况,而正如所示,只传递二个参数的状态,乃至会在C#方无缘无故的卡死:

typedef void (*CalibrationProgressCallback)(double percent);

改为__stdcall的点子就可以缓和难题,表明如下:

typedef  void(__stdcall *Render)(double* data, BOOL* color);

以下来自网络的一段_cdecl和__stdcall的演说,必须铭记:

  1. __cdecl

即所谓的C调用规则,按从右至左的逐个压参数入栈,由调用者把参数弹出栈。切记:对于传送参数的内部存款和储蓄器栈是由调用者来保卫安全的。重返值在EAX中。因而,对于象printf那样变参数的函数必须用这种规则。编写翻译器在编写翻译的时候对这种调用规则的函数生成修饰名的饿时候,仅在输出函数名前增加二个下划线前缀,格式为_functionname。

  1. __stdcall

按从右至左的次第压参数入栈,由被调用者把参数弹出栈。_stdcall是Pascal程序的缺省调用格局,平常用于Win32
Api中,切记:函数自身在脱离时清空货仓,重回值在EAX中。  __stdcall调用约定在输出函数名前拉长三个下划线前缀,前面加上八个“@”符号和其参数的字节数,格式为[email protected]。如函数int
func(int a, double
b)的梳洗名是[email protected]

之所以,从C++
dll中回调函数给C#传递数据,必须由C#函数在选择完数据后(退出函数时)自身清空仓库!所C++中的回调函数指针应该如下概念:

typedef void (_stdcall *CalibrationProgressCallback)(double percent);

总结:

C++通过callback向C#传递数据必须小心以下几点:

1、C++中的回调函数必须用_stdcall标志,使用stdcall格局回调;

2、假使是数组,必须用 [MarshalAs(UnmanagedType.LPArray, SizeConst =
23)]标志参数,钦点为数组且标志数经理度;

3、C#方必须说澳优(Ausnutria Hyproca)个变量,用来指向C++的回调指针函数,制止被C#回收掉。

from:

未来比较流行C#与C++融合:C#做GUI,开采功能高,C++做运算,运转作用高,二者兼得。
不过C++与C#肯定期存款在数据…

但是C++与C#早晚存在多少交互,C#与C++dll的多寡交互平素都以二个令人脑仁疼的标题。

从调用格局看也可能有二种情状:

1、C#调用C++函数

这种情况用的可比多,数据流向可以是C#流向C++,通过参数将数据传递给C++(如:SetData(double[]
data));也得以是C++流向C#(如:GetData(double[] data))。

2、C++ Callback

这种状态是C++中经过Callback的办法调用C#代码,类似于C++做过部分拍卖后向C#发送事件,事件能够辅导数量(如管理后的数据)。则C++中定义函数指针的方式是:

typedef  void(*Render)(double* data, BOOL* color);

 

C#用作委托,定义的函数被C++ callback:

public delegate void RenderCallback([MarshalAs(UnmanagedType.LPArray,
SizeConst =23)]double[] data, [MarshalAs(UnmanagedType.LPArray,
SizeConst = 23)]int[] colors);

相对注意,delegate中的double[]数组一定要增长马尔斯halAs标志,标识为传送数组,而且必须钦定传递的数额,借使不标记数量,则每趟只传递贰个数值,这一个难点折磨我很久才解决!

别的注意事项:

1、如何在C#中保持C++的函数指针

回调函数的另四个注意事项是向C++ dll传递回调函数指针的主题材料

要是有个函数向C++dll传递指针:

public delegate void EKFRenderCallback(string data, string colors);

public class EKFLib
{
    [DllImport("EKFLib.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    public static extern void SetRenderCallback(EKFRenderCallback render);

  

C#中如下传递被回调的函数:

public void RenderCallback(string data, string color)
{
    // rendering
}

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    EKFLib.SetRenderCallback(RenderCallback);
    EKFLib.Init();
}

  

那即使没什么难题,可是透过SetRenderCallback()传入到C++的指针不受托管代码管理,在C#中以为此指针对象未被此外轮代理公司码引用,GC做垃圾回收时,将会把C#本土的空指针回收,导致C++不可能实践回调,出现“CallbackOnCollectedDelegate”错误:

对“MotionCapture!MotionCapture.EKFRenderCallback::Invoke”类型的已垃圾回收委托开始展览了回调。那说不定会招致应用程序崩溃、损坏和数据丢失。向非托管代码传递委托时,托管应用程序必须让这几个委托儿和保育持活动状态,直到确信不会重新调用它们。

微软官方网站的例子是调节GC回收机制,那是个相比鸠拙的章程,特别自然的措施是把信托定义成两性子质,指向一个new出来的callback,然后再把这几个callback传递进C++dll中,那样,在C#端有对象引用,保障了GC不会回收此callback:

public void RenderCallback(string data, string color)
{
    // rendering
}

private EKFRenderCallback render;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
    render = new EKFRenderCallback(RenderCallback);
    EKFLib.SetRenderCallback(render);
    EKFLib.Init();
}

 

2、__stdcall与_cdecl传递数据

近来一个类别是透过C++ 的
dll做高速运算,然后把结果数据经过Callback的艺术回调给C#(分界面部分),结果两次三番在C#中收取回调事件后就直接挂掉(程序直接在毫不提示的情景下退出,未有别的调节和测试消息可能提醒)。

致使难题的由来是,暗中同意意况下,C++中如下概念的函数指针,暗许是以_cdecl形式调用的:

typedef  void(*Render)(double* data, BOOL* color);

这种情景下,参数旅舍是由调用者(C++一侧)维护的,在C++调用此回调函数后,会把参数弹出堆栈而释放,导致C#读取数据时现身莫明其妙的不当。

如上是回调函数字传送递数组大概出现的图景,而正如所示,只传递三个参数的景况,乃至会在C#方无缘无故的卡死:

typedef void (*CalibrationProgressCallback)(double percent);

改为__stdcall的章程就能够消除难题,评释如下:

typedef  void(__stdcall *Render)(double* data, BOOL* color);

以下来自互联网的一段_cdecl和__stdcall的讲演,必须记住:

  1. __cdecl

即所谓的C调用规则,按从右至左的一一压参数入栈,由调用者把参数弹出栈。切记:对于传送参数的内部存款和储蓄器栈是由调用者来维护的。再次来到值在EAX中。由此,对于象printf这样变参数的函数必须用这种规则。编写翻译器在编写翻译的时候对这种调用规则的函数生成修饰名的饿时候,仅在输出函数名前增进三个下划线前缀,格式为_functionname。

  1. __stdcall

按从右至左的逐个压参数入栈,由被调用者把参数弹出栈。_stdcall是帕斯Carl程序的缺省调用格局,平时用于Win32
Api中,切记:函数自个儿在退出时清空仓库,再次来到值在EAX中。  __stdcall调用约定在输出函数名前增加一个下划线前缀,后边加上二个“@”符号和其参数的字节数,格式为_functionname@number。如函数int
func(int a, double b)的梳洗名是_func@12

所以,从C++
dll中回调函数给C#传递数据,必须由C#函数在行使完数据后(退出函数时)自身清空商旅!所C++中的回调函数指针应该如下概念:

typedef void (_stdcall *CalibrationProgressCallback)(double percent);

总结:

C++通过callback向C#传递数据必须小心以下几点:

1、C++中的回调函数必须用_stdcall标志,使用stdcall格局回调;

2、假若是数组,必须用 [MarshalAs(UnmanagedType.LPArray, SizeConst =
23)]标识参数,内定为数组且标志数老董度;

3、C#方必须说惠氏(WYETH)个变量,用来指向C++的回调指针函数,制止被C#回收掉。

from:

相关文章