最近做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);