最近做的專案,需要在c# 中呼叫c++ 寫的dll,因為c# 預設的編碼方式是unicode,而呼叫的dll規定只處理utf8編碼格式的字串,dll中的輸入引數型別char*被我marshal成byte,輸出引數型別char**被我marshal成了string(c++和c#之間的型別轉換請參閱相關資料),於是我就經歷了無數次用於接收時的string-->string(utf8-->unicode)和用於傳送時的string-->byte(unicode-->utf8)這樣頻繁的編碼轉換,期間多次出現中文亂碼,看來編碼轉換並非想象中那麼簡單,今天花時間google一番,原來以前的理解確實不夠深,存在很多誤區,現整理如下:
(一)、encoding和charset
為什麼先提這兩個,實屬問題之源。在c#中包裝dll的時候,dllimportattribute當中的選項charset著實讓我糊塗了很久,msdn曰:規定封送字串應使用何種字符集,其中列舉值有ansi和unicode,我真不知道到底改選哪乙個。於是乎, google一番,encoding這棵救命草被我找到,同時也釋疑了不少疑惑。
首先,字符集不同於編碼,以前總將它們混為一談,charset是字符集,encoding是編碼。字符集是字元的集合,規定這個集合裡有哪些字元,每個字元都有乙個整數編號(只是編號不是編碼);而編碼是用來規定字元編號如何與二進位制互動,每個「字元」分別用乙個位元組還是多個位元組儲存。啊嗚,原來這樣,那我這裡接觸到的ansi、unicode、utf8等等等等究竟是怎麼回事呢,藉此機會,一**竟!^_^
(二)、ansi、unicode、utf8、bala bala
提到字符集,有ascii、gb2312、gbk、gb18030、big5、jis等等多種,與此相對應的編碼方式為ascii、gb2312、gbk、gb18030、big5、jis(囧,難怪糊塗如我般的人如此多),但是unicode字符集卻有多種編碼方式:utf-8、 utf-7、utf-16、 unicodelittle、unicodebig。原來如此,字符集與編碼原來是這個樣子。ˇˍˇ|||
那ansi又是什麼呢?
ansi:系統編碼的發展經歷了三個階段,asciiàansi(不同國家語言本地化)àunicode(標準化),原來ansi編碼也好,ansi字符集也好,都是指本地化的東西,在簡體中文系統下,ansi 編碼代表 gb2312 編碼,windows下自帶的記事本程式,預設的就是ansi編碼。呵呵,那偶就去試試吧,輸入「anhui合肥」(不含引號),儲存編碼方式選「ansi」,檢視,哦,9個位元組,明白了,原來ansi編碼保留了對ascii編碼的相容,當遇到ascii字元時,採用單位元組儲存,當遇到非ascii編碼時,採用雙位元組表示(gb2312編碼)。
(三)、dllimportattribute中的charset列舉值選擇
對字符集和編碼的概念清楚了以後,終於可以研究c#中呼叫非託管的dll的方法咯。從託管應用程式去呼叫非託管**,如果charset=unicode,則dll中的介面函式將出現的所有字串(包括引數和返回值)視為unicode字符集,ansi一樣的道理。真不錯,了解到這裡總算有點撥雲見日的感覺了!o(∩_∩)o
好像目前需要了解的知識點都差不多了,終於可以開始來解決我的問題了, (*^__^*)。回顧整理一下,第一:我需要呼叫非託管**,第二:非託管**只處理utf8編碼格式的字元,第三,萬惡的中文亂碼,介面函式簽名中,無論引數還是返回值,接收到或是傳送出的字串都含有中文。呵呵,看來上面的知識應該可以解決這個問題了:首先接收字串,因為接收到的是utf8編碼格式,charset屬性肯定要設定成unicode了,接收後要正確顯示中文,在當前系統中,需要將編碼方式轉換成gb2312,即encoding.default;另外關於傳送字元陣列,因為無論是c#中預設的unicode編碼方式,還是dll處理時規定的utf8編碼方式,都是unicode字符集的一種編碼方式,所以charset也要設定成unicode,只是要在傳送前需要將字元陣列轉換一下編碼方式。以下附帶兩個簡單的函式實現。
// 轉換接收到的字串
public string utf8tounicode(string recvstr)
byte tempstr = encoding.utf8.getbytes(recvnotify);
byte tempdef = encoding.convert(encoding.utf8, encoding.default, tempstr);
string msgbody = encoding.default.getstring(tempdef);
return msgbody;
// 轉換要傳送的字元陣列
public byte unicodetoutf8(string sendstr)
string tempstr = encoding.utf8.getstring(sendstr);
byte msgbody = encoding.utf8.getbytes(temputf8);
return msgbody;
總結一下,本文因專案完成的需要,難免存在侷限性,只討論了兩種情況:unicode編碼的字串轉utf8格式的字元陣列,以及utf8格式的字串轉unicode格式的字串,上面兩個方法均通過測試,至此總算解決了中文亂碼的問題。平台呼叫的知識點很多,只有真正掌握必需的基礎知識和平台呼叫的原理,才能做到活學活用,我要繼續努力。本文的理解若有不當之處,請網友們多有指教!( ^_^ )/~~
C 呼叫C Dll封裝時遇到的小問題
c 的乙個dll,裡面有個方法,無返回型別,乙個輸出引數 char 兩個輸入引數。呼叫時遇到點小問題,總結一下。錯誤的呼叫1,直接崩潰 dllimport upgradeserverdll.dll public static extern void upgrade getpatchinifile o...
編寫C 呼叫的C DLL
最近一段時間,經常遇到這些問題,前一陣子研究了一下,沒有記下來,沒想到最近研究又有些不記得了,今天把它寫下來以備忘。一般我們提供給其他語言呼叫的dll,都是用c或者c 編寫,然後封裝。我這邊也是採用的c 首先有幾個注意點 1 如果功能很簡單,或者不使用第三方庫 如mfc自帶的庫 建立乙個win32的...
C 呼叫C DLL的方式
動態鏈結庫 dll 是乙個包含可由多個程式同時使用的 和資料的庫,dll不是可執行檔案。可以說在windows作業系統中隨處可見,開啟主分割槽盤下的system32。在一些專案中,如果有大量運算或者涉及大量演算法時通常使用c或c 語言封裝成乙個dll,開放一些介面供其他程式呼叫。下面是寫的乙個簡單的...