欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++通過Callback向C#傳遞數(shù)據(jù)的方法

 更新時間:2018年01月06日 10:20:44   作者:特務小強  
下面小編就為大家分享一篇C++通過Callback向C#傳遞數(shù)據(jù)的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧

現(xiàn)在比較流行C#與C++融合:C#做GUI,開發(fā)效率高,C++做運算,運行效率高,二者兼得。

但是C++與C#必然存在數(shù)據(jù)交互,C#與C++dll的數(shù)據(jù)交互從來都是一個讓人頭疼的問題。

從調(diào)用方式看也有兩種情況:

1、C#調(diào)用C++函數(shù)

這種情況用的比較多,數(shù)據(jù)流向可以是C#流向C++,通過參數(shù)將數(shù)據(jù)傳遞給C++(如:SetData(double[] data));也可以是C++流向C#(如:GetData(double[] data))。

2、C++ Callback

這種情況是C++中通過Callback的方式調(diào)用C#代碼,類似于C++做過一些處理后向C#發(fā)送事件,事件可以攜帶數(shù)據(jù)(如處理后的數(shù)據(jù))。則C++中定義函數(shù)指針的方式是:

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

C#作為委托,定義的函數(shù)被C++ callback:

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

千萬注意,delegate中的double[]數(shù)組一定要加上MarshalAs標記,標記為傳遞數(shù)組,而且必須指定傳遞的數(shù)量,如果不標記數(shù)量,則每次只傳遞一個數(shù)值,這個問題折磨我很久才搞定!

其他注意事項:

1、如何在C#中保持C++的函數(shù)指針

回調(diào)函數(shù)的另一個注意事項是向C++ dll傳遞回調(diào)函數(shù)指針的問題

假設有個函數(shù)向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#中如下傳遞被回調(diào)的函數(shù):

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++無法執(zhí)行回調(diào),出現(xiàn)“CallbackOnCollectedDelegate”錯誤:

對“MotionCapture!MotionCapture.EKFRenderCallback::Invoke”類型的已垃圾回收委托進行了回調(diào)。這可能會導致應用程序崩潰、損壞和數(shù)據(jù)丟失。向非托管代碼傳遞委托時,托管應用程序必須讓這些委托保持活動狀態(tài),直到確信不會再次調(diào)用它們。

微軟官網(wǎng)的例子是控制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傳遞數(shù)據(jù)

最近一個項目是通過C++ 的 dll做高速運算,然后把結果數(shù)據(jù)通過Callback的方式回調(diào)給C#(界面部分),結果總是在C#中接到回調(diào)事件后就直接掛掉(程序直接在毫無提示的情況下退出,沒有任何調(diào)試信息或者提示)。

導致問題的原因是,默認情況下,C++中如下定義的函數(shù)指針,默認是以_cdecl方式調(diào)用的:

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

這種情況下,參數(shù)堆棧是由調(diào)用者(C++一側)維護的,在C++調(diào)用此回調(diào)函數(shù)后,會把參數(shù)彈出堆棧而釋放,導致C#讀取數(shù)據(jù)時出現(xiàn)莫名其妙的錯誤。

以上是回調(diào)函數(shù)傳遞數(shù)組可能出現(xiàn)的情況,而如下所示,只傳遞一個參數(shù)的情況,甚至會在C#方莫名其妙的卡死:

typedef void (*CalibrationProgressCallback)(double percent);

改為__stdcall的方式即可解決問題,申明如下:

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

以下來自網(wǎng)絡的一段_cdecl和__stdcall的解釋,必須牢記:

1. __cdecl

即所謂的C調(diào)用規(guī)則,按從右至左的順序壓參數(shù)入棧,由調(diào)用者把參數(shù)彈出棧。切記:對于傳送參數(shù)的內(nèi)存棧是由調(diào)用者來維護的。返回值在EAX中。因此,對于象printf這樣變參數(shù)的函數(shù)必須用這種規(guī)則。編譯器在編譯的時候?qū)@種調(diào)用規(guī)則的函數(shù)生成修飾名的餓時候,僅在輸出函數(shù)名前加上一個下劃線前綴,格式為_functionname。

2. __stdcall

按從右至左的順序壓參數(shù)入棧,由被調(diào)用者把參數(shù)彈出棧。_stdcall是Pascal程序的缺省調(diào)用方式,通常用于Win32 Api中,切記:函數(shù)自己在退出時清空堆棧,返回值在EAX中?! _stdcall調(diào)用約定在輸出函數(shù)名前加上一個下劃線前綴,后面加上一個“@”符號和其參數(shù)的字節(jié)數(shù),格式為_functionname@number。如函數(shù)int func(int a, double b)的修飾名是_func@12

所以,從C++ dll中回調(diào)函數(shù)給C#傳遞數(shù)據(jù),必須由C#函數(shù)在使用完數(shù)據(jù)后(退出函數(shù)時)自己清空堆棧!所C++中的回調(diào)函數(shù)指針應該如下定義:

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

總結:

C++通過callback向C#傳遞數(shù)據(jù)必須注意以下幾點:

1、C++中的回調(diào)函數(shù)必須用_stdcall標記,使用stdcall方式回調(diào);

2、如果是數(shù)組,必須用 [MarshalAs(UnmanagedType.LPArray, SizeConst = 23)]標記參數(shù),指定為數(shù)組且標記數(shù)組長度;

3、C#方必須申明一個變量,用來指向C++的回調(diào)指針函數(shù),避免被C#回收掉。

以上這篇C++通過Callback向C#傳遞數(shù)據(jù)的方法就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • C# 實現(xiàn)視頻監(jiān)控系統(tǒng)(附源碼)

    C# 實現(xiàn)視頻監(jiān)控系統(tǒng)(附源碼)

    這篇文章主要介紹了C# 如何實現(xiàn)視頻監(jiān)控系統(tǒng),幫助大家更好的理解和使用c#,感興趣的朋友可以了解下
    2021-02-02
  • C#常用目錄文件操作類實例

    C#常用目錄文件操作類實例

    這篇文章主要介紹了C#常用目錄文件操作類,實例分析了C#針對目錄的讀取、檢測及查找等相關操作技巧,非常具有實用價值,需要的朋友可以參考下
    2015-03-03
  • C#如何實現(xiàn)用戶名與密碼登錄

    C#如何實現(xiàn)用戶名與密碼登錄

    這篇文章主要介紹了C#如何實現(xiàn)用戶名與密碼登錄問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • C#生成條形碼圖片的簡單方法

    C#生成條形碼圖片的簡單方法

    這篇文章主要介紹了C#生成條形碼圖片的簡單方法,實例分析了了條形碼圖片的生成原理與實現(xiàn)方法,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-01-01
  • 解析C#拼接Json串的幾種方法

    解析C#拼接Json串的幾種方法

    本文主要介紹了C#拼接Json串的幾種方法,主要包括了4種方式,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-06-06
  • C#中的GDI+圖像編程詳解

    C#中的GDI+圖像編程詳解

    本文詳細講解了C#中的GDI+圖像編程,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-05-05
  • C# 泛型深入理解介紹

    C# 泛型深入理解介紹

    在上一個專題中介紹了C#2.0 中引入泛型的原因以及有了泛型后所帶來的好處,然而上一專題相當于是介紹了泛型的一些基本知識的,對于泛型的性能為什么會比非泛型的性能高卻沒有給出理由,所以在這個專題就中將會介紹原因和一些關于泛型的其他知識
    2012-11-11
  • C#編寫的Base64加密和解密類

    C#編寫的Base64加密和解密類

    本文給大家匯總介紹了幾種C#編寫的Base64加密和解密類的代碼,從簡單到復雜,都能夠?qū)崿F(xiàn)功能,有需要的小伙伴根據(jù)自己的項目需求參考下吧。
    2015-05-05
  • C#實現(xiàn)過濾html標簽并保留a標簽的方法

    C#實現(xiàn)過濾html標簽并保留a標簽的方法

    這篇文章主要介紹了C#實現(xiàn)過濾html標簽并保留a標簽的方法,文中的自定義函數(shù)采用正則過濾實現(xiàn)了該功能,是非常實用的技巧,需要的朋友可以參考下
    2014-09-09
  • C#中Convert.ToDecimal()報錯問題的解決

    C#中Convert.ToDecimal()報錯問題的解決

    這篇文章主要給大家介紹了關于C#中Convert.ToDecimal()報錯問題的解決方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。
    2017-08-08

最新評論