I have a library (dll) which exposes a class along with its constructors which are supposed to be used in other modules (exe and dll). I am able to instantiate that class from other library modules but not exe modules. I get the linker error - 'error LNK2019: unresolved external symbol' during linking. I am confused why linking succeeds when used in other library projects and not exe project. Can somebody help me with this?
the following is the class declaration:
class __declspec(dllimport) MyException
{
public:
MyException(unsigned int _line, const char *_file, const char *_function, MyExceptionType _type, const wchar_t* _message = 0, ...);
};
This is the whole error:
error LNK2019: unresolved external symbol "__declspec(dllimport) public: __cdecl MyException::MyException(unsigned int,char const *,char const *,enum MyException::MyExceptionType,unsigned short const *,...)" (_imp??0MyException##QAA#IPBD0W4MyExceptionType#0#PBGZZ) referenced in function "public: __thiscall MyClassUsesMyException::MyClassUsesMyException(class SomeClass *,int)" (??0MyClassUsesMyException##QAE#PAVSomeClass##H#Z)
MyClassUsesMyException is being instantiated in 'MyApp.cpp'.
Thanks, Rakesh.
Update: wchar_t Not Always Native
After a fairly long exchange of information and getting a little more info from the OP, the problem is essential this:
class __declspec(dllimport) MyException
{
public:
MyException(unsigned int _line,
const char *_file,
const char *_function,
MyExceptionType _type,
const wchar_t* _message = 0, // <<== note wchar_t type
...);
};
Visual C++ can be configured to either treat wchar_t as a native type or not. When not treated as a native type, unsigned short is the specified macro substitution for wchar_t. The linker was complaining about the above function being unresolvable, but what really caught my eye was this at the tail of the undefined symbol:
,unsigned short const *,...)
Note the unsigned short. This hinted to me that the compiler was using non-native wchar_t when compiling the EXE. I considered it possible that maybe the DLL was compiled with wchar_t configured as native, thereby introducing a different signature and thus not matching up at link time.
If you're surprised this was the problem, imagine how surprised Rakesh and I were =P
Original Answer
That class should be in a single common header with preprocessor logic to determine proper import/export state of the declaration. Like so:
MyDLL.h
#ifndef MYDLL_H
#define MYDLL_H
// setup an import/export macro based on whether the
// DLL implementing this class is being compiled or
// a client of the DLL is using it. Only the MyDLL.DLL
// project should define MYDLL_EXPORTS. What it is
// defined as is not relevant. *That* it is defined *is*.
#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif
class MYDLL_API MyException
{
// your class definition here...
};
#endif
Then, in your DLL project that implements the exception (and only in that project), add MYDLL_EXPORTS to the preprocessor defines list in your project configuration.
Related
I have a Visual C++ solution, using Visual Studio 2017, which contains 5 projects:
SpikeConfig
SpikeEngine
SpikeRenderer
SpikeUI
SpikeUtils
In SpikeUtils, I have a header _SpikeEngineObject.h:
#pragma once
#ifdef DLL_SPIKEUTILS
#define SPIKEUTILS_EXPORT __declspec(dllexport)
#else
#define SPIKEUTILS_EXPORT __declspec(dllimport)
#endif
#include "GUID.h"
namespace SpikeUtils
{
class SPIKEUTILS_EXPORT _SpikeEngineObject
{
public:
const std::string & _SpikeEngineId()
{
return _SpikeRef.Value();
}
private:
SpikeUtils::GUID _SpikeRef = SpikeUtils::GUID::Generate();
};
}
The file GUID.h included looks like this:
#pragma once
#include <iostream>
#ifdef DLL_SPIKEUTILS
#define SPIKEUTILS_EXPORT __declspec(dllexport)
#else
#define SPIKEUTILS_EXPORT __declspec(dllimport)
#endif
namespace SpikeUtils
{
class SPIKEUTILS_EXPORT GUID final
{
public:
GUID(GUID const & other) = default;
GUID& operator=(GUID& other) = default;
static GUID Generate();
std::string const & Value();
private:
GUID(std::string const & value) : value(value)
{}
std::string value;
};
}
I am omitting the implementation of GUID.cpp because I don't think it's relevant.
Now, in SpikeUI, I have a class Drawable, that just inherits from _SpikeEngineObject.h
#pragma once
#include "_SpikeEngineObject.h"
#ifdef DLL_SPIKEUI
#define SPIKEUI_EXPORT __declspec(dllexport)
#else
#define SPIKEUI_EXPORT __declspec(dllimport)
#endif
namespace SpikeUI
{
namespace UI
{
struct SPIKEUI_EXPORT Drawable : SpikeUtils::_SpikeEngineObject
{
....
};
}
}
Obviously, all the respective DLL_ defines have been put inside each individual project's C/C++ -> Preprocessor -> Preprocessor definitions, so the projects should build with the appropriate dllimport / dllexport macro.
But when I try and build SpikeUI, I get linker errors like:
LNK2019 unresolved external symbol "__declspec(dllimport) public: __cdecl
SpikeUtils::_SpikeEngineObject::_SpikeEngineObject(void)" (__imp_??
0_SpikeEngineObject#SpikeUtils##QEAA#XZ) referenced in function "public:
__cdecl SpikeUI::UI::Drawable::Drawable(enum SpikeUI::UI::DrawableType)" (??
0Drawable#UI#SpikeUI##QEAA#W4DrawableType#12##Z)
and
LNK2019 unresolved external symbol "__declspec(dllimport) public: __cdecl
SpikeUtils::_SpikeEngineObject::~_SpikeEngineObject(void)" (__imp_??
1_SpikeEngineObject#SpikeUtils##QEAA#XZ) referenced in function "int
`public: __cdecl SpikeUI::UI::Drawable::Drawable(struct UI::Drawable::dtor$0
const &)'::`1'::dtor$0" (?dtor$0#?0???0Drawable#UI#SpikeUI##QEAA#AEBU012##Z#4HA)
An interesting fact is that Visual Studio even highlights which macro will be used, and for example GUID.h does highlight the dllexport macro, but _SpikeEngineObject.h highlights the dllimport macro for some reason.
Searching through SO and MSDN, it looks like this macro pattern should work, but for some reason it's not consistent on my project.
How can I solve the linker errors?
Answering my own question here:
The SpikeUI project has a dependency on the SpikeUtils project, and it is linked against the .lib and .dll project that is outputed by building the SpikeUtils project.
Now, the problem was that the class _SpikeEngineObject was a header-only class (ie no .cpp for the class). Hence, I am assuming that the SpikeUtils.lib did not contain any symbols for the constructor / destructor etc. of the _SpikeEngineObject, so the linker was having problems linking it.
I solved the problem by adding a .cpp file for the class and implementing dumb constructor / destructor so that the compiler would generate these symbols, and everything was fine.
I have a C++ Visual Studio 2013 console application which is supposed to make use of a DLL MyDLLlib.dll which I have written. MyDLLlib is written in C. One of the functions is called Get_Version. The prototype is
const char *Get_Version();
I put this at the top of the source files to make use of the prototype:
extern "C"{
#include "MyDLLlib.h"
}
If in the function is called in the main as this
printf("version %s\n",Get_Version());
then it works.
However if I add a class with some static methods and a static method makes a call to Get_Version()
const char * ret = Get_Version();
then I get a link error:
Error 1 error LNK2019: unresolved external symbol
"__declspec(dllimport) char * __cdecl Get_Version(void)" (__imp_?Get_Version##YAPADXZ)
referenced in function "private: static class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl ServiceDispatch::decoder_Get_Version(class StringBuffer &)"
(?decoder_Get_Version#ServiceDispatch##CA?AV?$basic_string#DU?$char_traits#D#std##V?$allocator#D#2##std##AAVStringBuffer###Z)
D:\devt\CplusPlus\VSTrials\Link_to_MyDLLlib\Link_to_MyDllLib\ServiceDispatch.obj Link_to_MyDLLlib``
I am using the same include.
Any clue as to what I might be doing wrong?
If you have CLASS_DECLSPEC defined always as __declspec(dllimport), this will not work for sure. Look at this sample:
DLL_header.h
#if defined( _BUILD_DLL )
# define DLLAPI __declspec(dllexport) //Export when building DLL
#else
# define DLLAPI __declspec(dllimport) //Import when using in other project
#endif
DLLAPI const char *Get_Version();
DLL_source.cpp
#include "Header.h"
const char *Get_Version()
{
return "1.1.0.4";
}
Build DLL with _BUILD_DLL defined.
Main.cpp
#include "DLL_header.h"
int main()
{
printf("%s\n", Get_Version());
return 0;
}
Build this, with _BUILD_DLL not defined.
In your case, it could be problem with extern "C" - you include header inside extern "C", which declares Get_Version() as having __cdecl linkage. But linker is searching for
__imp_?Get_Version##YAPADXZ
Which is a mangled (C++) name. Is your DLL a C or C++ project? If your DLL is build as C project (not C++), put extern "C" on Get_Version()'s declaration with this #ifdef:
#ifdef __cplusplus
extern "C" {
#endif
DLLAPI const char *Get_Version();
#ifdef __cplusplus
}
#endif
Either way, remove extern "C" from around the #include. Also, check if .lib file for this DLL is attached to project as dependency.
I'm using Visual Studio 2012, managed C++, to make a bridge between a third party SDK and our system which is written in C#. I have succesfully wrapped and consumed several functions from said SDK. Except one, which only result in a Unresolved External Error.
The SDK's header file defines the function's signature:
#if defined WIN32
#if defined BUILD_ADS_SHARED_LIB
#define ADS_LINK_SPEC __declspec (dllexport)
#define ADS_CALLING_CONVENTION __stdcall
#elif defined USE_ADS_SHARED_LIB
#define ADS_LINK_SPEC __declspec (dllimport)
#define ADS_CALLING_CONVENTION __stdcall
#else
#define ADS_LINK_SPEC
#define ADS_CALLING_CONVENTION
#endif
#else
#define ADS_LINK_SPEC
#define ADS_CALLING_CONVENTION
#endif
DatabaseResult ADS_LINK_SPEC ADS_CALLING_CONVENTION
createDatabase(
const Settings& settings, Artec::SdkDatabase::iDatabase *& instance);
The error says:
Error 10 error LNK2028: unresolved token (0A000089) "enum Artec::SdkDatabase::DatabaseResult __cdecl Artec::SdkDatabase::createDatabase(class Artec::SdkDatabase::Settings const &,class Artec::SdkDatabase::iDatabase * &)" (?createDatabase#SdkDatabase#Artec##$$FYA?AW4DatabaseResult#12#ABVSettings#12#AAPAViDatabase#12##Z) referenced in function "private: static enum Artec::SdkDatabase::DatabaseResult __clrcall Broadway3dWrapper::Broadway3dWrapper::GetConn(wchar_t const *,wchar_t const *,wchar_t const *,wchar_t const *,char const *,class Artec::SdkDatabase::iDatabase * &)" (?GetConn#Broadway3dWrapper#1#$$FCM?AW4DatabaseResult#SdkDatabase#Artec##PB_W000PBDAAPAViDatabase#34##Z) C:\bioap\tfs\Identitum\Dev\src\BA.Identitum.Devices.Broadway3d\Broadway3dWrapper.obj BA.Identitum.Devices.Brodway3D
So it's looking for the mangled name:
?createDatabase#SdkDatabase#Artec##$$FYA?AW4DatabaseResult#12#ABVSettings#12#AAPAViDatabase#12##Z
Making a little dumpbin on the referenced dll, I found there is in fact a function called like that exported, thing is the name is mangled slightly different:
?createDatabase#SdkDatabase#Artec##YG?AW4DatabaseResult#12#ABVSettings#12#AAPAViDatabase#12##Z
Can anyone help me here? I cannot contact the SDK vendor, and I'm completely lost here.
The difference between those two lies in the calling convention section.
createDatabase#SdkDatabase#Artec##YG?AW4DatabaseResult#12#ABVSettings#12#AAPAViDatabase#12##Z is stdcall: enum Artec::SdkDatabase::DatabaseResult __stdcall Artec::SdkDatabase::createDatabase(class Artec::SdkDatabase::Settings const &,class Artec::SdkDatabase::iDatabase * &)
The demangler I used does not understand ?createDatabase#SdkDatabase#Artec##$$FYA?AW4DatabaseResult#12#ABVSettings#12#AAPAViDatabase#12##Z, but the part where they differ (##$$FYA? vs ##YG?) is the calling convention (if I change YG to YF, the calling convention changes and nothing else does).
Change your declaration of the function to return-type __stdcall function-name[(argument-list)].
When you included the header file, did you #define USE_ADS_SHARED_LIB explicitly or on the compiler command line? Are you targeting 32 bit windows?
Use the undname.exe utility to undecorate names. It is looking for:
enum Artec::SdkDatabase::DatabaseResult
__cdecl
Artec::SdkDatabase::createDatabase(
class Artec::SdkDatabase::Settings const &,
class Artec::SdkDatabase::iDatabase * &
)
The one you found is:
enum Artec::SdkDatabase::DatabaseResult
__stdcall
Artec::SdkDatabase::createDatabase(
class Artec::SdkDatabase::Settings const &,
class Artec::SdkDatabase::iDatabase * &
)
Everything matches, except the calling convention, __cdecl vs __stdcall. Note how the SDK header allows this to happen, it doesn't raise a stink when neither BUILD_ADS_SHARED_LIB nor USE_ADS_SHARED_LIB is #defined. And that will produce a __cdecl function. Bad idea btw.
So very high odds that you simply forgot to define USE_ADS_SHARED_LIB. Project + Properties, C/C++, Preprocessor, Preprocessor Definitions setting.
I have a DLL and a main executable, and the main executable is not linking in the symbols from the import library of the DLL anymore, and the declspec in the DLL .map file is not matching, nor is the mangled C++ name matching. I can't figure out why although I have done the usual things you do when you can't get C++ stuff to link.
My headers define something like this:
#ifdef MY_MODULE
#undef CLASS_EXPORT
#define CLASS_EXPORT __declspec(dllexport)
#else
#undef CLASS_EXPORT
#define CLASS_EXPORT __declspec(dllimport)
#endif
My classes seem to be exporting properly, but the .map file indicates some strange thing I think is wrong:
6 ?CreateDataArea##YAKPAGPBD111PAVCObject##K#Z (unsigned long __cdecl
CreateDataArea(unsigned short *,char const *,char const *,char const *,
char const *,class CObject *,unsigned long))
The mangled name above has ##YAK...
The link error is:
1>Device.obj : error LNK2019: unresolved external symbol
"__declspec(dllimport) public: unsigned long __thiscall
CCommonMemory::CreateDataArea(unsigned short *,char const *,char const *,
char const *,char const *,class CObject *,unsigned long)"
(__imp_?CreateDataArea#CCommonMemory##QAEKPAGPBD111PAVCObject##K#Z) referenced
in function "public: __thiscall CDevice::CDevice(void)" (??0CDevice##QAE#XZ)
So why is the mangled name ##QAEK different when I have the declspec macro configured and why is the .map file showing __thiscall calling convention when the macro defines __declspec(dllexport)?
It's exactly like I forgot to do the __declspec(dllimport/dllexport) macro, yet I did it.
I tried putting CLASS_EXPORT on EACH method exported, and I tried it in the first line of the class declaration with no change.
It looks like the definition for CreateDataArea() isn't being scoped to the class (ie., you've left off a CCommonMemory:: when defining the function).
If you look at what's getting into the map file, you'll see that it's a __cdecl function without the class name 'attached'.
I created a DLL project and successfully built it. I then tried to use the DLL in another Project, TEST, and I am getting the following error.
Error 1 error LNK2001: unresolved external symbol "public: void __thiscall SnoMessage::setRawMessageName(class ATL::CStringT<wchar_t,class StrTraitMFC_DLL<wchar_t,class ATL::ChTraitsCRT<wchar_t> > >)" (?setRawMessageName#SnoMessage##QAEXV?$CStringT#_WV?$StrTraitMFC_DLL#_WV?$ChTraitsCRT#_W#ATL#####ATL###Z)
I added the required lib in the linker properties, and I also added the header files in the TEST include directory. So the function is being recognized, but it keeps giving those errors. The DLL is comprised of the following files
SnoMessage.h
#pragma once
#include "StdAfx.h"
class SnoMessage
{
public:
__declspec(dllexport) SnoMessage(void);
__declspec(dllexport) ~SnoMessage(void);
__declspec(dllexport) void setRawMessageName(CString messageName);
__declspec(dllexport) void setRawMessageType(CString messageType);
__declspec(dllexport) void setRawMessageAttributes(std::map<CString,CString> attributes);
__declspec(dllexport) CString getRawMessageName();
__declspec(dllexport) CString getRawMessageType();
__declspec(dllexport) std::map<CString,CString> getRawMessageAttributes();
private:
CString messageName;
CString messageType;
std::map<CString,CString> attributes;
};
SnoMessage.cpp
#include "stdafx.h"
#include "SnoMessage.h"
SnoMessage::SnoMessage(void)
{
}
SnoMessage::~SnoMessage(void)
{
}
void SnoMessage::setRawMessageName(CString messageName){
this->messageName = messageName;
}
void SnoMessage::setRawMessageType(CString messageType){
this->messageType = messageType;
}
void SnoMessage::setRawMessageAttributes(std::map<CString,CString> attributes){
this->attributes = attributes;
}
CString SnoMessage::getRawMessageName(){
return messageName;
}
CString SnoMessage::getRawMessageType(){
return messageType;
}
std::map<CString,CString> SnoMessage::getRawMessageAttributes(){
return attributes;
}
And in test I am doing the following:
test.cpp
// test.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "SnoMessage.h"
int _tmain(int argc, _TCHAR* argv[])
{
SnoMessage *msg = new SnoMessage();
msg->setRawMessageName("TEST");
return 0;
}
Let me know if you need more info, thanks.
In your dll define this in some header you want to use for your export defs...
MyExports.h
#ifdef SNOMESSAGE_EXPORTS
#define SNOMESSAGE_API __declspec(dllexport)
#else
#define SNOMESSAGE_API __declspec(dllimport)
#endif
Now in your dll you just define SNOMESSAGE_EXPORTS, then when your dll is compiled your class and methods will be visible to the exe. But when you include those same headers in the exe the Macro will import them instead of export.
//In the DLL this is == to export, in the executable this is import. Problem solved.
class SNOMESSAGE_API SnoMessage
{
public:
//...
};
You no longer need to export each member, just the class.
I would mark the whole class as exported, not just its member functions. Also, following the advice of this conversation, you need to specify __declspec(dllecport) or __declspec(dllimport) based on whether you are including the header in the DLL or the code that uses the DLL; and define the guarding macro in the DLL project.
When you compile the DLL you should have __declspec(dllexport), but when you compile exe you should have __declspec(dllimport). The easiest way to do this is to have a #define somewhere that has different value when "in DLL" and "out of DLL". Also do export the whole class instead of individual methods.
There is a case when dll compile use C call but exe use standard call, the link in x64 has no problem, but when using win32 will show this link error 2001. For that situation just use C call for both dll and exe for win32 platform (https://learn.microsoft.com/en-us/cpp/error-messages/tool-errors/name-decoration?view=msvc-160).