C#调用C和C++函数的一点区别
最近做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);
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构