C#呼叫C和C++函式的一點區別
阿新 • • 發佈:2022-04-28
最近做U800電話的二次開發,需要呼叫廠商的C函式庫來打電話,後來想加入通話錄音功能,但發現程式預設生產的WAV檔案過大,又找了個WAV轉MP3的C++函式庫程式,出了點問題。下面是轉MP3的程式介面(標頭檔案):
#ifndef _MP3ENC_H_
#define _MP3ENC_H_
int mp3_enc(const char* inWavName,int nRate,const char* outMP3Name);
#endif
按照C#呼叫非託管程式的約定,宣告一個對應的C#函式介面:
[DllImport("mp3enc.dll", CharSet = CharSet.Ansi)] public static extern int mp3_enc(string inWavName, int nRate, string outMP3Name);
然後這樣呼叫改函式:
mp3_enc(txtWavFile.Text, 32, "222.mp3");
結果出現這樣的異常資訊:
1 對 PInvoke 函式“U800Test!U800Test.Form1::mp3_enc”的呼叫導致堆疊不對稱。原因可能是託管的 PInvoke 簽名與非託管的目標籤名不匹配。請檢查 PInvoke 簽名的呼叫約定和引數與非託管的目標籤名是否匹配。
跟原來呼叫C函式庫的方法仔細對比,發現沒有區別,而C函式程式碼卻可以正常使用: 打電話的C函式介面:
USBDLL_API int _stdcall StartDial(int iDevIdx,const char* szDest); //傳送撥號命令
對應的C#函式介面:
[DllImport("UsbDll.dll", CharSet = CharSet.Ansi)]
public static extern int StartDial(int iDevIdx,string szDest);
再看看C++的呼叫函式的錯誤資訊,難道是C++字串型別不一致?需要C++使用Unicode 字元?於是將C#的函式介面改成:
[DllImport("mp3enc.dll", CharSet = CharSet.Unicode)] public static extern int mp3_enc(string inWavName, int nRate, string outMP3Name);
結果C#程式直接崩潰,連Try....Catch.... 都沒用。
最後,在網上搜索了半天,發現有這個說法:
DllImport還有一個CallingConvention的屬性,預設值是CallingCovention.Stdcall, 此處更改成Cdecl(c/c++預設呼叫方式)就可以了。VS2010下必須得指定這個屬性才能執行, 同樣的程式碼在VS2008下卻不存在這樣的問題, 奇怪 ...
正確的C++ 函式C#呼叫介面應該是這樣:
[DllImport("mp3enc.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern int mp3_enc(string inWavName, int nRate, string outMP3Name);