C#:請向C++傳遞全域委派物件以用於回調

來源:互聯網
上載者:User

手頭有個C++的DLL,是公司的一個遠程通話功能組件。長久以來,這個DLL對外只有三個操作介面。而從合理的角度來說,這個DLL本應再提供一些回調介面或事件之類的東西,用於在通話狀態被動改變時通知外層應用程式。不過銀行裡的規矩多多,不能用ActiveX;而使用回調,卻又似不那麼的穩定,尤其在與C#互動時。所以才一直就這麼用著。

直到最近,外層應用程式的開發人員提意見抱怨這個DLL。於是,一狠心,決定向這個DLL添加回調的支援。雖然不能確定之前使用回調不穩定的問題在哪,但現有資料和之前自己單獨寫的測試卻都表明C#向C++傳遞的回調是可以正常工作的。因此,必定是這個DLL的什麼地方存在錯誤才會不穩定,大不了搭上一個周末的時間,總應該能解決吧。

一般來說,實現回調的要點就兩點:

1. 回調應加上WINAPI或CALL關鍵字,以聲明其調用方式;

2. 注意參數類型尤其是長度,在C++和C#中的聲明要一致;避免使用指標和參考型別。

由於之前已經單獨測試過,所以就差在DLL中實戰演練了。花了一天時間,根據他們的要求改好了程式。為了避免遇到傳說中的住在迷宮裡的那個會吃迷路的人的魔鬼。其間還打了幾針預防針,先在定義回調時先排除了任何字元參數,只用整數;然後去掉了之前的那個中間DLL。原來的核心DLL是MFC擴充動態連結程式庫,不能直接用在C#中,所以又在外邊套了個正規DLL。雖然間接調用DLL很正常,但讓結構更簡單,總沒有壞處。

一切妥當,開始調試,改掉幾個小問題後,發現程式竟然正常的跑起來了,回調的輸出歡快的顯示到螢幕上了。又各式各樣的環境各測了幾遍,依然OK,看來這次運氣不差,哈。接下來打包,寫介面和回調說明文檔,畫狀態圖。

在發出去前,想起來又改了改一些調試和日誌資訊,然後又神經質的測試了一把。事後證明,這是一個讓人後悔也慶幸的動作。因為,這次遇到Demo的老兄Demon了,哎,怎一個慘字了得.....

幸虧本命年的我早有時刻面對打擊的覺悟,早前沒有得意忘形,要不這下心情落差大去了。額,不過,來就來吧,有周末兩天做後盾,不信搞不定你。

看看Demon老兄說了些啥:SYSTEM.WINDOWS.FORMS.NI.DLL 0xC0000005 access violation。

完全摸不著頭腦,只能慢慢來了。

先檢查了一遍DLL程式,應該沒有問題,所有的回調也都是在主線程上觸發的,應該跟線程也沒有關係。

再來確定到底是C++ DLL的還是C# EXE的問題,把DLL和EXE在DEBUG和Release下各編譯了一份,然後組合運行了一下,發現若且唯若使用EXE的Release版程式時,會出現該異常。那麼,就應該是C#程式的問題了。

這隻是一個非常簡單的樣本,實在想不明白代碼可能會有什麼問題。而且還是Debug正常,Release異常。先改改編譯選項試試,把Optimze Code關了試試,問題照舊;再把Define Debug constant開了,這就跟Debug版本的編譯選項完全一樣了,還是不行。看來此路不通了。

看看Google同學有沒有遇到過這樣的問題,把Demon老兄說的搜了一下,結果沒有相關資料…看來這下有得頭疼了。

忽然想起似乎剛才DLL和EXE組合測試的時刻似乎出過一個挺不尋常的提示。再去找找看,發現Debug版的DLL和Release版的EXE一起運行會有這樣的提示:

File: i386\chkesp.c
Line: 42

The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.

 

再搜尋一下,這次倒有不少同樣不幸的人,不過正如那句話“不幸的人各有各的不幸”,他們的原因似乎跟我的情況扯不上啥關係。

這下沒招了,再求助Baidu一下,看看大家用C++回調C#代碼都遇到些啥情況。結果試了好幾個關鍵字,都一無所獲,正當我感覺鴨梨很大的時候,突然間,用關鍵字“C++ C# 回調 委託”搜到了一個有點意思的文章 C#處理C++庫回調報錯_Unmanaged 程式碼傳遞委託被記憶體回收,雖然這位老兄什麼都沒說明,不過貼上來的異常內容很特別:

檢測到 CallbackOnCollectedDelegate
Message: 對“HBVideoParser!Videocomm.Video.HB.HBSDK+SrcDataParseCBHandler::Invoke”類型的已記憶體回收委託進行了回調。這可能會導致應用程式崩潰、損壞和資料丟失。向Unmanaged 程式碼傳遞委託時,託管應用程式必須讓這些委託保持活動狀態,直到確信不會再次調用它們。

 

向C++傳遞的委派物件回記憶體回收了,導致回調時異常?感覺有點怪怪,不過死馬當活馬醫吧,改Demo,傳了一個全域委派物件給DLL,再測試~哈,回調輸出又歡快滴出來了~

看來果真如此了,確認一下,改回原來的傳遞臨時對象的方式,在Demo的Main函數裡加個異常捕獲,發現確實異常會傳遞到此處,內容則是“Object reference not set to an instance of an object”。雖然與上述的不一致,不過應該就是一回事啦。

現在想想,這也不難理解。C#中通過引用計數來判斷對象是否可被回收,在Demo中,建立的臨時委派物件只是被臨時使用了一下,沒有任何引用,雖然在C++ DLL中儲存了,但儼然那已經不屬於Demo的範圍了。因此被記憶體回收很正常。

再想到之前自己單獨寫的測試似乎並沒有發現這樣的問題,那又是為什麼呢?難道因為程式佔用內在太少還沒來得及進行記憶體回收操作?再去翻出來,點住調用按鈕不放,果然,在輸出三四十條資訊之後程式異常了。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.