VC++ 使用 dllimport/dllexport 宏在多个项目中包含 headers 时出现链接器错误
VC++ linker error when using dllimport/dllexport macro to include headers in multiple projects
我有一个 Visual C++ 解决方案,使用 Visual Studio 2017,其中包含 5 个项目:
- SpikeConfig
- SpikeEngine
- SpikeRenderer
- SpikeUI
- SpikeUtils
在 SpikeUtils 中,我有一个 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();
};
}
包含的文件 GUID.h 如下所示:
#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;
};
}
我省略了 GUID.cpp 的实施,因为我认为它不相关。
现在,在 SpikeUI 中,我有一个 class Drawable,它继承自 _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
{
....
};
}
}
显然,所有相应的 DLL_ 定义都已放入每个单独项目的 C/C++ -> 预处理器 -> 预处理器定义中,因此项目应该使用适当的 dllimport / dllexport 宏构建。
但是当我尝试构建 SpikeUI 时,我收到如下链接器错误:
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)
和
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[=17=]
const &)'::`1'::dtor[=17=]" (?dtor[=17=]@?0???0Drawable@UI@SpikeUI@@QEAA@AEBU012@@Z@4HA)
一个有趣的事实是 Visual Studio 甚至突出显示将使用哪个宏,例如 GUID.h 确实突出显示 dllexport 宏,但 _SpikeEngineObject.h 出于某种原因突出显示 dllimport 宏.
通过 SO 和 MSDN 搜索,看起来这个宏模式应该有效,但由于某种原因,它在我的项目中不一致。
如何解决链接器错误?
在这里回答我自己的问题:
SpikeUI 项目依赖于 SpikeUtils 项目,并且链接到 .lib 和 .dll 项目通过构建 SpikeUtils 项目输出。
现在,问题是 class _SpikeEngineObject 是 header-only class(即 [=32 没有 .cpp =]).因此,我假设 SpikeUtils.lib 不包含 _SpikeEngineObject 的构造函数/析构函数等的任何符号,因此链接器在链接它时遇到问题。
我通过为 class 添加一个 .cpp 文件并实现哑构造函数/析构函数来解决问题,以便编译器生成这些符号,一切都很好。
我有一个 Visual C++ 解决方案,使用 Visual Studio 2017,其中包含 5 个项目:
- SpikeConfig
- SpikeEngine
- SpikeRenderer
- SpikeUI
- SpikeUtils
在 SpikeUtils 中,我有一个 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();
};
}
包含的文件 GUID.h 如下所示:
#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;
};
}
我省略了 GUID.cpp 的实施,因为我认为它不相关。
现在,在 SpikeUI 中,我有一个 class Drawable,它继承自 _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
{
....
};
}
}
显然,所有相应的 DLL_ 定义都已放入每个单独项目的 C/C++ -> 预处理器 -> 预处理器定义中,因此项目应该使用适当的 dllimport / dllexport 宏构建。
但是当我尝试构建 SpikeUI 时,我收到如下链接器错误:
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)
和
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[=17=]
const &)'::`1'::dtor[=17=]" (?dtor[=17=]@?0???0Drawable@UI@SpikeUI@@QEAA@AEBU012@@Z@4HA)
一个有趣的事实是 Visual Studio 甚至突出显示将使用哪个宏,例如 GUID.h 确实突出显示 dllexport 宏,但 _SpikeEngineObject.h 出于某种原因突出显示 dllimport 宏.
通过 SO 和 MSDN 搜索,看起来这个宏模式应该有效,但由于某种原因,它在我的项目中不一致。
如何解决链接器错误?
在这里回答我自己的问题:
SpikeUI 项目依赖于 SpikeUtils 项目,并且链接到 .lib 和 .dll 项目通过构建 SpikeUtils 项目输出。
现在,问题是 class _SpikeEngineObject 是 header-only class(即 [=32 没有 .cpp =]).因此,我假设 SpikeUtils.lib 不包含 _SpikeEngineObject 的构造函数/析构函数等的任何符号,因此链接器在链接它时遇到问题。
我通过为 class 添加一个 .cpp 文件并实现哑构造函数/析构函数来解决问题,以便编译器生成这些符号,一切都很好。