前言 關於混合C 和C++的編程方式,本人之前寫過一篇博客(參見 "混合語言編程:C 使用原生的Directx和OpenGL" ),在之前的博客中,介紹了在C 的Winform和WPF下使用原生的Direct和OpenGL進行繪圖,主要使用的方式是聲明一個函數為導出函數,然後就可以在C 中使用這個函
前言
關於混合C#和C++的編程方式,本人之前寫過一篇博客(參見混合語言編程:C#使用原生的Directx和OpenGL),在之前的博客中,介紹了在C#的Winform和WPF下使用原生的Direct和OpenGL進行繪圖,主要使用的方式是聲明一個函數為導出函數,然後就可以在C#中使用這個函數。
存在的問題
之前的方式使C#調用C/C++成為可能,但是存在很多缺點,主要表現在以下幾個方面:
- 使用
extern "C" _declspec(dllexport)
的聲明方式只能定義C函數,無法直接使用C++的類,功能不夠強大。 - 參數傳遞很麻煩,尤其是傳入數組時,經常會出現參數類型錯誤或者數組長度不正確,很不靈活。
- 需要寫重覆性的代碼,在C#代碼中需要重覆聲明C/C++寫的DLL中的函數,如果在C/C++代碼中定義了結構體,還需要在C#中重覆聲明,處理參數類型又是一個麻煩的事情。
- 需要手動拷貝DLL到C#程式的目錄下,如果忘記拷貝了,程式在運行時會報DLL未找到的錯誤。
- 非常不利於調試,無法在C/C++代碼中進行斷點跟蹤調試。同時對C/C++代碼修改編譯後,需要拷貝DLL到C#程式目錄,否則C#程式調用的還是修改之前的DLL。
前段時間開發的一個應用程式中需要控制兩個數採卡(SP Divece 的ADQ和SDR),官方提供了C和C++的驅動,可以使用C/C++對數採卡進行控制。我最開始還是使用了之前聲明導出函數的方式進行開發,用C語言實現,但是隨著功能的複雜和代碼的增加,上面一系列問題越來越嚴重。
發現新大陸(公共語言運行時編譯)
在奮鬥解決各種Bug的時候突然在一次搜索時找到了公共語言運行時編譯。所謂公共語言運行時編譯,就是允許應用程式和組件使用公共語言運行時 (CLR) 中的功能。找到MSDN上的相關文檔:
有了公共語言運行時編譯,在C#程式集中就可以引用C++開發的DLL,並且使用C++的類就和使用使用C#類是一樣的,還可以直接斷點調試,以上問題全部解決。
很快,我就把之前用C寫的代碼改寫成了C++的代碼,啟用CLR,並刪掉了C#中重覆的代碼。
指針問題
使用C++開發就會經常使用到指針,但C#沒有指針(一般情況,其實C#是有指針的,只不過預設被關閉了)。在C#中要傳遞一個指針至少有兩種方式:
使用
stackalloc
在棧上分配記憶體塊,這類似於C的malloc
和C++的new
(當然還是有區別的),詳細信息可參考stackalloc(C# 參考)。使用
fixed語句
固定變數的指針,C#中之所以不讓用指針,就是因為由於垃圾回收機制會導致變數重定位,變數重定位後,之前的指針也就不再指向這個變數了,所以C#在這種情況下是要禁止使用指針。而fixed 語句
禁止垃圾回收器重定位可移動的變數,併在執行該語句期間“固定”此變數。固定變數的位置後就可以使用指針了,詳細信息可參考fixed 語句(C# 參考)。
需要提醒的是,這兩種方式都需要在不安全的上下文中使用,關於不安全上下文,可參考unsafe(C# 參考)。
結語
本文主要記錄我在做項目中發現的問題、解決問題所使用到相關的技術,有效地解決了C#調用C++的問題。當然,其中還有很多細節並沒有深入研究,可能會存在更好的方式。