【图文】[新手]C++ 动态库导出函数名“乱码”及解决
刚接触C++,在尝试从 dll 中导出函数时,发现导出的函数名都“乱码”了。
导出过程如下:
新建一个Win32项目:
新建的解决方案里有几个导出的示例:
// 下列 ifdef 块是创建使从 DLL 导出更简单的 // 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 DLLEXPORT_EXPORTS // 符号编译的。在使用此 DLL 的 // 任何其他项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将 // DLLEXPORT_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的 // 符号视为是被导出的。 #ifdef DLLEXPORT_EXPORTS #define DLLEXPORT_API __declspec(dllexport) #else #define DLLEXPORT_API __declspec(dllimport) #endif // 此类是从 dllExport.dll 导出的 class DLLEXPORT_API CdllExport { public: CdllExport(void); // TODO: 在此添加您的方法。 }; extern DLLEXPORT_API int ndllExport; DLLEXPORT_API int fndllExport(void);
于是我什么都不做,直接生成,并且在C#里导入看看能否调用,嗯……错误来了:
找不到入口点?难道是没导出么?我们用“Dependency Walker”来看看:
Oh, shit, WTF is this? 导出是导出了,不过怎么都乱码了?
右键选择“Undecorate C++ Functions”之后才出现了真面目:
不过我们的目的是要在C#中使用,而不是用眼睛在 Dependency 里面看啊!嗯,既然入口点的名字都变了,要不我们在 C# 中手动指定入口点试试?
不错,成功了,我们终于可以使用 C++ dll里导出的函数了。
不过,这些乱码到底是什么东西?百度一下很轻松地找到了答案:
DLL(动态库)导出函数名乱码含义
C++编译时函数名修饰约定规则:
__stdcall调用约定:
1、以"?"标识函数名的开始,后跟函数名;
2、函数名后面以"@@YG"标识参数表的开始,后跟参数表;
3、参数表以代号表示:
X--void
D--char
E--unsigned char
F--short
H--int
I--unsigned int
J--long
K--unsigned long
M--float
N--double
_N--bool
....
PA--表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,以"0"代替,一个"0"代表一次重复;
4、参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前;
5、参数表后以"@Z"标识整个名字的结束,如果该函数无参数,则以"Z"标识结束。
其格式为"?functionname@@YG*****@Z"或"?functionname@@YG*XZ",例如
int Test1(char *var1, unsigned long)-----?Test1@@YGHPADK@Zvoid Test2()-----"?Test2@@YGXXZ"
__cdecl调用约定:
规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的"@@YG"变为"@@YA"。
__fastcall调用约定:
规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的"@@YG"变为"@@YI"。如果要用DEF文件输出一个"C++"类,则把要输出的数据和成员的修饰名都写入.def模块定义文件
所以... 通过def文件来导出C++类是很麻烦的,并且这个修饰名是不可避免的
虽然有约定的含义,但这也真够麻烦的!我不禁想,我们之前导入 User32.dll,Shell32.dll 等等这些动态库的函数的时候,那些EntryPoint没见这么麻烦啊,怎么回事?还是万能的百度……“在到处函数之前加上“extern "C"”就行了!”,我们来试试:
// 下列 ifdef 块是创建使从 DLL 导出更简单的 // 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 DLLEXPORT_EXPORTS // 符号编译的。在使用此 DLL 的 // 任何其他项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将 // DLLEXPORT_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的 // 符号视为是被导出的。 #ifdef DLLEXPORT_EXPORTS #define DLLEXPORT_API __declspec(dllexport) #else #define DLLEXPORT_API __declspec(dllimport) #endif // 此类是从 dllExport.dll 导出的 class DLLEXPORT_API CdllExport { public: CdllExport(void); // TODO: 在此添加您的方法。 }; extern "C" DLLEXPORT_API int ndllExport; extern "C" DLLEXPORT_API int fndllExport(void);
注意和之前对比,最后两行有变化。编译生成,运行 C# 项目:
没有指定 EntryPoint 了,成功!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决