传递数据

2019-10-01 14:35 来源:未知

现行反革命可比流行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[]数组必供给增加MarshalAs标志,标志为传送数组,并且必须钦赐传递的数码,若是不标志数量,则每一趟只传递三个数值,这些主题材料折磨笔者比较久才化解!

另外注意事项:

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#方必需说圣元个变量,用来指向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:

TAG标签:
版权声明:本文由金沙澳门官网4166发布于文物考古,转载请注明出处:传递数据