最近做U800電話的二次開發,需要調用廠商的C函數庫來打電話,後來想加入通話錄音功能,但發現程序默認生產的WAV文件過大,又找了個WAV轉MP3的C++函數庫程序,出了點問題。下面是轉MP3的程序接口(頭文件):
_MP3ENC_H_ mp3_enc( * inWavName, nRate, *
按照C#調用非托管程序的約定,聲明一個對應的C#函數接口:
[DllImport(, CharSet = mp3_enc( inWavName, nRate, outMP3Name);
然後這樣調用改函數:
mp3_enc(txtWavFile.Text, , );
結果出現這樣的異常信息:
對 PInvoke 函數“U800Test!U800Test.Form1::mp3_enc”的調用導致堆棧不對稱。原因可能是托管的 PInvoke 簽名與非托管的目標簽名不匹配。請檢查 PInvoke 簽名的調用約定和參數與非托管的目標簽名是否匹配。
跟原來調用C函數庫的方法仔細對比,發現沒有區別,而C函數代碼卻可以正常使用:
打電話的C函數接口:
USBDLL_API _stdcall StartDial( iDevIdx, * szDest);
對應的C#函數接口:
[DllImport(, CharSet = StartDial( iDevIdx, szDest);
再看看C++的調用函數的錯誤信息,難道是C++字符串類型不一致?需要C++使用Unicode 字符?於是將C#的函數接口改成:
[DllImport(, CharSet = mp3_enc( inWavName, nRate, outMP3Name);
結果C#程序直接崩潰,連Try....Catch.... 都沒用。
最後,在網上搜索了半天,發現有這個說法:
DllImport還有一個CallingConvention的屬性,默認值是CallingCovention.Stdcall, 此處更改成Cdecl(c/c++默認調用方式)就可以了。VS2010下必須得指定這個屬性才能運行, 同樣的代碼在VS2008下卻不存在這樣的問題, 奇怪 ...
正確的C++ 函數C#調用接口應該是這樣:
[DllImport(, CharSet = CharSet.Ansi, CallingConvention = mp3_enc( inWavName, nRate, outMP3Name);