C++ Mangled符号含$$F与库中符号不匹配的原因及解决咨询
问题分析与解决方案
你遇到的LNK2028错误核心是编译器生成的函数符号与链接库中的符号调用约定不匹配,具体差异在于编译器生成的符号多了$$F标识,我们来一步步拆解:
符号差异对比
编译器为CThostFtdcMdApi::CreateFtdcMdApi()生成的符号:
?CreateFtdcMdApi@CThostFtdcMdApi@@$$FSAPEAV1@PEBD_N1@Z
链接的x64 lib文件中dumpbin得到的符号:
?CreateFtdcMdApi@CThostFtdcMdApi@@SAPEAV1@PEBD_N1@Z
两者的核心差异是前者包含$$F——这是MSVC名字修饰中**__clrcall调用约定**的标识,而lib中的符号使用的是标准C++静态成员函数的修饰规则(S开头表示static)。
完整错误信息
Error LNK2028 unresolved token (0A000021) "public: static class CThostFtdcMdApi * __cdecl CThostFtdcMdApi::CreateFtdcMdApi(char const *,bool,bool)" (?CreateFtdcMdApi@CThostFtdcMdApi@@$$FSAPEAV1@PEBD_N1@Z) referenced in function "public: void __cdecl CTPMarketData::Start(char const *,char const *,char const *,char const *)" (?Start@CTPMarketData@@$$FQEAAXPEBD000@Z) CTPLib_cpp
dumpbin输出结果
61C __IMPORT_DESCRIPTOR_thostmduserapi 862 __NULL_IMPORT_DESCRIPTOR 9A0 thostmduserapi_NULL_THUNK_DATA D94 ?CreateFtdcMdApi@CThostFtdcMdApi@@SAPEAV1@PEBD_N1@Z D94 __imp_?CreateFtdcMdApi@CThostFtdcMdApi@@SAPEAV1@PEBD_N1@Z E2C ?GetApiVersion@CThostFtdcMdApi@@SAPEBDXZ E2C __imp_?GetApiVersion@CThostFtdcMdApi@@SAPEBDXZ C0E ??1CThostFtdcMdApi@@IEAA@XZ C0E __imp_??1CThostFtdcMdApi@@IEAA@XZ B8E ??0CThostFtdcMdApi@@QEAA@XZ B8E __imp_??0CThostFtdcMdApi@@QEAA@XZ B08 ??0CThostFtdcMdApi@@QEAA@AEBV0@@Z B08 __imp_??0CThostFtdcMdApi@@QEAA@AEBV0@@Z C8E ??4CThostFtdcMdApi@@QEAAAEAV0@AEBV0@@Z C8E __imp_??4CThostFtdcMdApi@@QEAAAEAV0@AEBV0@@Z D18 __imp_??_7CThostFtdcMdApi@@6B@
为什么会出现这个差异?
看你的Visual Studio编译命令行,你启用了/clr选项(托管C++编译):
d:\Program Files(x86)\Microsoft Visual Studio\2017\Enterprise\VC\Tools\MSVC\14.14.26428\bin\HostX86\x86\CL.exe / c / AI"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETFramework\v4.6.1\" / AI"C:\Program Files (x86)\Windows Kits\10\References" / AI"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETFramework\v4.6.1\Facades\" / Zi / clr / nologo / W3 / WX - / diagnostics:classic / Od / Oy - / D WIN32 / D _DEBUG / D _WINDLL / D _UNICODE / D UNICODE / EHa / MDd / GS / fp : precise / Zc : wchar_t / Zc : forScope / Zc : inline / Yu"stdafx.h" / Fp"Debug\CTPLib_cpp.pch" / Fo"Debug\" / Fd"Debug\vc141.pdb" / TP / FU"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETFramework\v4.6.1\mscorlib.dll" / FU"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETFramework\v4.6.1\System.Data.dll" / FU"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETFramework\v4.6.1\System.dll" / FU"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETFramework\v4.6.1\System.Xml.dll" / analyze - / FC / errorReport : prompt / clr : nostdlib AssemblyInfo.cpp CTPMarketData.cpp CTPMarketDataWrapper.cpp CTPSpi.cpp
当使用/clr编译时,MSVC会默认将非托管函数的调用约定改为__clrcall(用于托管代码与非托管代码交互),但你链接的lib是用纯非托管C++编译的,使用的是标准__cdecl调用约定,因此符号不匹配触发链接错误。
解决方案
你可以通过以下几种方式解决:
- 局部禁用托管编译:在调用
CreateFtdcMdApi()的代码块前后添加#pragma managed指令,强制该部分按非托管方式编译:#pragma managed(push, off) _pUserApi = CThostFtdcMdApi::CreateFtdcMdApi(); #pragma managed(pop) - 显式指定调用约定:在
CThostFtdcMdApi的头文件声明中,为CreateFtdcMdApi添加__cdecl调用约定:class CThostFtdcMdApi { public: static __cdecl CThostFtdcMdApi* CreateFtdcMdApi(const char*, bool, bool); // 其他成员... }; - 检查平台一致性:注意到你的编译命令行是x86的CL.exe,但链接的是x64 lib,确保项目平台设置(x86/x64)与lib的平台完全一致,避免跨平台链接问题。
C++符号解密参考
对于MSVC的名字修饰符号,你可以使用以下工具和规则来解密:
- 使用
undname工具:这是MSVC自带的符号解密工具,在Developer Command Prompt中运行:
会直接输出函数的原型:undname ?CreateFtdcMdApi@CThostFtdcMdApi@@$$FSAPEAV1@PEBD_N1@Zpublic: static class CThostFtdcMdApi * __clrcall CThostFtdcMdApi::CreateFtdcMdApi(char const *,bool,bool) - 核心修饰规则:
@@$$F:标识__clrcall调用约定S:标识静态成员函数APEAV1:返回CThostFtdcMdApi*(A表示指针,PEAV表示指向类的指针,1表示当前类)PEBD:参数为const char*_N1:两个bool类型参数(N表示bool,1表示数量)
内容的提问来源于stack exchange,提问作者Brian Rice
相关产品推荐
相关产品推荐

