2
votes

I have the following function call in my unmanaged c++ class:

_pUserApi = CThostFtdcMdApi::CreateFtdcMdApi();

The compiler seems to be generating this symbol:

?CreateFtdcMdApi@CThostFtdcMdApi@@$$FSAPEAV1@PEBD_N1@Z

But when I do a dumpbin on the .lib file I am linking with I see this symbol:

?CreateFtdcMdApi@CThostFtdcMdApi@@SAPEAV1@PEBD_N1@Z

The difference is the first one has an additional $$F in it.

Are there compiler options that would account for this difference?... is there any kind of reference to decipher the mangling?

Note: this is an x64 lib file (and I'm compiling with x64 chosen.

Full Error:

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

Full 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@

The command line generated by visual studio:

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

3
Can you post information such as the toolchain being used (the versions of the command line programs is more precise than the Visual Studio version) and the command line options used to compile and link for both the program and the library?Michael Burr
I don't know about the library (not mine)... but my options which are the default (in Configuration Properties > C/C++ > Command Line > All Options) are: /Yu"stdafx.h" /GS <different options>Brian Rice
Not sure what the <different options> is about... but that is what it's showing me.Brian Rice
Also, Agner Fog has compiled impressive documentation of various C++ compilers name mangling algorithms: agner.org/optimize/calling_conventions.pdf. Unfortunately, I can't see where the $$F sequence for Microsoft C++ is in that document (but I certainly could have missed it - the name mangling recipes are complex). Perhaps it's something new?Michael Burr
And it is VS 2017... and I created a Visual C++ CLR library... but the class that calls this function is an unmanaged class.Brian Rice

3 Answers

3
votes

Microsoft compilers come with an undname utility:

with the $$F:

C:\>undname ?CreateFtdcMdApi@CThostFtdcMdApi@@$$FSAPEAV1@PEBD_N1@Z
Microsoft (R) C++ Name Undecorator
Copyright (C) Microsoft Corporation. All rights reserved.

Undecoration of :- "?CreateFtdcMdApi@CThostFtdcMdApi@@$$FSAPEAV1@PEBD_N1@Z"
is :- "public: static class CThostFtdcMdApi * __ptr64 __cdecl CThostFtdcMdApi::CreateFtdcMdApi(char const * __ptr64,bool,bool)"

without:

c:\>undname ?CreateFtdcMdApi@CThostFtdcMdApi@@SAPEAV1@PEBD_N1@Z
Microsoft (R) C++ Name Undecorator
Copyright (C) Microsoft Corporation. All rights reserved.

Undecoration of :- "?CreateFtdcMdApi@CThostFtdcMdApi@@SAPEAV1@PEBD_N1@Z"
is :- "public: static class CThostFtdcMdApi * __ptr64 __cdecl CThostFtdcMdApi::CreateFtdcMdApi(char const * __ptr64,bool,bool)"

Unfortunately, undname decodes both to the same function signature. Some more digging is in order...

PS: An equivalent tool for GCC is c++filt

0
votes

So... it did turn out to be the difference between a native library and a CLR library. When the calls were made from withing the CLR library it inserted the $$F into the mangled name, but not when the calls were made from within a native library.

Interestingly... I made a class / function in a native library that created the object... that fixed it... plus I was able to call other member functions from within the CLR library code and that worked!

0
votes

I had the same problem. I had an abstract base class with a non virtual method. CLR could not link against the non virtual method, but when I changed the function to virtual it worked!

So $$f has something do with the method being virtual, but the name unmangler does not display it so.