程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> C#調用C++的dll及MATLAB的dll的方法(一)

C#調用C++的dll及MATLAB的dll的方法(一)

編輯:關於C++

為了記錄踩坑的過程,避免以後再踩坑,居然專門開通了這麼專業的技術博客,正好督促自己以後好好研究技術。

最近需要做一個界面系統來包裝一下之前做的人臉屬性識別的模型,希望用戶隨機選取一張圖像(後面會實現攝像頭拍現場圖像),系統自動給出該圖像中所包含的人臉屬性(有沒有戴眼鏡,有沒有戴帽子之類的)。其中人臉屬性預測之前需要進行人臉識別以及人臉對齊等操作,人臉屬性識別是由C++寫的,人臉對齊是由MATLAB寫的,考慮到界面的友好性以及開發的難易性,最終選了C#作為開發語言。(不要問我為啥不用Java,因為我沒裝eclipse,沒配Java環境,並且C++和C#都是用vs編譯,俺個人覺得這樣方便些,其實主要是受某位師兄鼓動。。。他說Java也不會比C#容易到哪去)。然後說說為什麼要用C#調用C++的dll和MATLAB的dll,直接混編不行麼。其實也行,就是更麻煩。。。代碼易讀性非常差,並且不好調試,相信我,我也試過,但最終放棄了。。。dll相對來說簡易許多,雖然封裝過程糾結了好幾天,但是使用起來像是黑盒子,你只要按照給定的參數類型輸入,就可以得到理想輸出,一兩句話搞定,也基本不需要配置環境啥的,如果有同學遇到和我類似的需求,可以考慮一下我的做法。

下面開始詳細詳細再詳細的記錄一下這幾天走過的各種坑,希望在幫助自己記錄的同時也能幫到一些人。

本機環境:win7 64位 + vs2013 + MATLAB2015
請大家看下面的調用過程之前一定看好你的本機環境是不是和我的一樣,如果不一樣,那我成功的方式你走一遍不一定成功。我當初Google的時候沒一個博客或者資料和我的環境一樣,所以都是摸著石頭過河,慢慢瞎鼓搗吧,不要放棄,總能搞出來的O(∩_∩)O。

如何用C#調用C++的dll
我也是最近要做這個演示的demo才知道C++是非托管代碼,如果C#(托管代碼)要用C++的話,必須要將C++的代碼包裝一下成為托管代碼,並編譯為dll文件才可以被C#正常調用。這裡貼上從百度裡查到的資料:
托管代碼 (managed code) :由公共語言運行庫環境(而不是直接由操作系統)執行的代碼。托管代碼應用程序可以獲得公共語言運行庫服務,例如自動垃圾回收、運行庫類型檢查和安全支持等。這些服務幫助提供獨立於平台和語言的、統一的托管代碼應用程序行為。
非托管代碼 (Unmanaged Code) :在公共語言運行庫環境的外部,由操作系統直接執行的代碼。非托管代碼必須提供自己的垃圾回收、類型檢查、安全支持等服務;它與托管代碼不同,後者從公共語言運行庫中獲得這些服務。非托管代碼的英文名是Unmanaged Code ,它是在公共語言運行庫環境的外部,由操作系統直接執行的代碼。
好啦,現在具體講一下怎麼做:
假設我有一個人臉檢測加5點定位的函數,希望可以在C#裡使用這個函數。函數的輸入是兩個字符串,分別表示輸入的人臉圖像路徑以及想要保存的5點定位路徑(一個txt裡面存著5點的坐標,用於後面人臉對齊)。首先,我們要把這函數包裝成一個類裡的成員函數,以便後面再次包裝為托管類。我們稱這個包裝函數的類為native類,為了區別後面的托管類clr類。Native寫好之後大概是長成這樣的:
\
圖片中的#pragma once是為了防止重復定義,後面的一些include文件你需要什麼就寫什麼。這裡有個地方需要注意,就是C#和C++的數據類型是不太一樣的。C++裡的string參數包裝好之後在C#那裡顯示的並不是string,而是C#裡的一種數據格式(忘記截圖了,反正看起來還挺復雜的),所以並不推薦大家用string作為輸入參數,用char*吧,這樣在C#那裡就會顯示為你需要輸入sbyte*的參數,這個類型的可以很容易將C#裡的string轉換過來,後面會有例子。
好了,有了h文件,根據需求實現一下cpp文件:

\
現在native類已經准備好了,可以進行托管類的包裝了。為了實現托管類,我們需要在在項目屬性那裡設置一下,不然編譯器識別不出來托管類的標志。

\

主要是改一下畫紅線的兩處,這樣生成解決方案後,不會像普通的項目一樣生成一個exe文件,而是生成一個dll文件,也就是後面我們在C#裡調用的dll文件。第二個畫紅線的地方可以幫助編譯器知道我們要做一個托管類。這樣設置好項目屬性後,就可以寫托管類了。(在這一步設置屬性的時候,我發現一個特別奇怪的事情,就是有時我一把這個屬性設置好保存之後,原來高亮提示的關鍵字全都沒有高亮提示了,好像是編譯器識別不出來了一樣,這個我也不知道怎麼回事,有時高亮消失,有時就不會。如果有人碰到和我一樣的問題,我只能說,親,你多試幾次吧。。。逮著哪次高亮沒消失就趕緊接著寫,機不可失啊!其實貌似不提示高亮也不影響生成dll文件或者後續調用,不用沒有高亮,寫代碼就木有提示啊!對或不對以及成員函數神馬的您就都自己寫吧。。。所以還是有提示比較好一點)。

好嘞,下面是最重要的寫托管類啦!其實很簡單哦!這一步在網上資料也比較多,如果我的不適合你的需求,你可以Google別人的呀!

\

這裡的public可以讓這個托管類在C#裡被看見,ref表示這是一個托管類。Public成員函數裡寫上你想要的函數(名字最好不一樣),然後private那裡一定要有一個native類的對象,這樣才可以通過托管類調用非托管類的函數呀!千萬別忘了!

然後,實現一下cpp文件:

\

 

兩處劃紅線的地方很重要,第一個紅線表明在托管類的構造函數裡實例化一個native類的對象,一定要實例化,不然後面用不了。第二個紅線表明在托管類的函數裡調用非托管類的函數。

如此一來,就可以點擊“生成解決方案”來生成一個dll啦。比如我的配置管理器裡寫的是release, x64,那生成的dll文件就在工程目錄下的x64目錄下的release目錄下。

\

 

這裡畫紅線的與你工程名字相同的dll就是你後面在C#裡需要用到的dll啦!如果你的C++項目還會額外用到什麼dll,lib之類的,在後面C#使用時也要一起拷貝過去,不然後提示dll依賴缺失之類的問題。

好啦!准備好dll,我們就可以轉眼去看看C#如何使用這個dll。

 

首先,你要把剛剛生成好的dll以及這個dll依賴的其他dll一起拷貝到C#的工程目錄下。比如我的C#配置管理器裡選的是release,Any Cpu,那應該是把這些dll一起拷貝到工程目錄下的bin目錄下的release目錄下。像這樣:

\

 

當然,我所說的C++的dll所依賴的dll不是指的那些opencv配置環境時的dll,而是你在函數裡需要用到的外部dll,比如我的函數用了人臉識別dll。像是配置opencv添加在項目附加文件那裡的dll以及opencv的頭文件好的,都會隨著你生成dll後一起包裝起來,所以是不是很神奇!是不是很省事!如果你連外部的dll都沒用到,那你直接把C++生成的dll拷貝過去就可以直接用啦!

除了把dll拷貝到上面說的C#的文件夾裡,還要將其放到C#當前工程目錄下,然後在C#項目的引用那裡選中這個dll將其引用到C#項目裡就可以使用啦!

 

\\

 

接下來說說怎麼在C#代碼裡使用C++的dll。像我這邊需要給C++的函數傳string的參數(當然,為了方便數據類型的轉換,我把C++函數裡的string換位了char*,前面已經提到了),然後我需要把兩種語言的數據類型統一起來,C++的char*對應的是C#裡的sbyte*,因此我先講C#裡的string轉為sbyte*,像這樣:

\

在這裡,如果想使用Marshal,C#編譯器會提示你這是一段不安全的代碼,如何避免錯誤提示呢,我們需要在這段代碼的外面加上unsafe標志,告訴編譯器我們知道不安全但我們就是要用!

\

 

如果此時它還提示你不安全,那麼就可以到項目屬性那裡設置一下:

\

 

在項目屬性的“生成”那裡會有“允許不安全代碼”的選項,勾上就可以啦!另外,換紅線的地方,提示一下各位童鞋。如果你的C++的dll編譯生成的時候是選的x64配置環境,那麼在這裡一定一定要把“首選32位”這個選項去掉,不然運行C#就會報錯。好了,下面講一下如何調用。

\

 

簡單吧!只有三行,第一行創建一個C++的dll封裝的托管類的對象(你托管類叫啥這裡就寫啥,不是非托管類的名字哦),然後第二行就是調用函數,第三行就是把sbyte*再轉會string。(不過int參數好像可以直接傳,不需要改類型,其他類型的參數我就不清楚了)。

至此,C#調用C++的dll就大功告成啦!!!鼓掌!!!雖說現在說起來好像不難,但是自己也是鼓搗了兩天,碰到各種問題,踩了各種坑才成功,如果大家按照我的方法沒成功,一是可能環境和我不一樣,二嘛,可能是你人品不好,哈哈,開玩笑。網上的資料千千萬,不一定會有你需要的,參考著來吧,自己慢慢試慢慢研究總會搞出來的。

本來想一篇寫好C++和MATLAB的調用。不過由於廢話多,篇幅過長,MATLAB的調用放到下一篇講。


  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved