1

我有一个共享库(Linux 中的 .so,Windows 中的 .dll)需要访问包含在它加载的任何可执行文件中的静态变量。这个变量恰好是一个类模板类型,并且在一个命名空间内。尽管将变量声明为“extern”(在 Windows 上为“__declspec(dllimport)”),VC10在链接 DLL 时为该变量给出“未解析的外部符号”错误。这对我来说似乎很奇怪,因为它确实应该解决,而是留给加载时间。

标题:

// a header demonstrating MSVC-compatible linkage
#ifdef _MSC_VER

#ifdef I_AM_DLL
#define TO_DLL_LINKAGE __declspec( dllimport )
#else
#define TO_DLL_LINKAGE __declspec( dllexport )
#endif

#else  // not MSVC
#define TO_DLL_LINKAGE
#endif

template<class T>
class TheClass
{
public:
   TheClass(T t) : value_(t) {}

   T value() const
   {
      return value_;
   }
private:
   T value_;
};

typedef TheClass<int> MyClass;

和DLL:

// a test library (DLL) for linkage experiment
#define I_AM_DLL
#include "theclass.hpp"

#include <iostream>

namespace foo {
extern TO_DLL_LINKAGE MyClass theObject;
}

void bar() {
   int i = foo::theObject.value();
   std::cout << "object value is " << i << std::endl;
}

错误:

错误 LNK2001:无法解析的外部符号“_ declspec(dllimport) class TheClass foo::theObject”( _imp_?theObject@foo@@3V?$TheClass@H@@A)

我想不用说这在 gcc 中可以正常工作。我还查看了许多类似的 StackOverflow 问题,但它们要么推荐我已经在做的事情,要么由于各种原因不适用(例如导出而不是导入,类而不是类实例等)。

我需要什么额外的魔法才能让 MSVC10 开心?谢谢。

4

1 回答 1

1

事实证明这里有两个基本问题:

  1. 在 Windows 上,符号解析是在链接时执行的,即使对于共享库也是如此
  2. dllimport 和 dllexport 是不对称的 - 所有 dllimports 都必须被解析

在 Linux 下使用 gcc 运行时,我的程序可以正常工作,因为对对象的“外部”引用在程序加载时被解析。共享库 (a .so) 列出了它正在导出和正在导入的符号,并且操作系统的程序加载器验证在程序启动时是否满足主程序和共享库的所有导入。

相比之下,在 Windows/VC++ 世界中,必须将满足导入符号的特定模块标识为链接共享库- 通常通过“导入库”或 .lib 文件。这不能推迟到程序加载时间。因此,链接步骤失败。

在我的特定情况下,我有一个可执行模块,它既需要来自共享库的符号,也需要向共享库提供(一个)符号。对于静态库和 gcc/Linux,这不是问题,但对于 Windows/VC++,这会产生循环依赖。有一个解决方案,但它需要一些额外的努力,在这个 StackOverflow 问题Microsoft 文档中进行了讨论。底线是要做到这一点需要一个更复杂的链接步骤,其中从可执行文件生成导入库以在共享库的链接阶段使用。如果您有__dllspec(dllexport) ,则会自动生成这样的库任何数据的存储类。最后一步是将此导入库添加到共享库 DLL 的链接阶段。

如果您像我一样是 CMake 用户,那么这个过程会通过一个名为ENABLE_EXPORTS的特殊目标属性变得更加容易,它允许库“链接到”可执行文件。

于 2012-11-28T20:38:41.840 回答