【问题标题】:Exporting static data in a DLL在 DLL 中导出静态数据
【发布时间】:2026-02-20 13:45:01
【问题描述】:

我有一个 DLL,其中包含一个带有静态成员的类。我使用__declspec(dllexport) 是为了利用这个类的方法。但是当我将它链接到另一个项目并尝试编译它时,我得到静态数据的“未解析的外部符号”错误。

例如 在 DLL 中,Test.h

class __declspec(dllexport) Test{
protected:
    static int d;
public:
    static void m(){int x = a;}
}

在 DLL 中,Test.cpp

#include "Test.h"

int Test::d;

在使用 Test 的应用程序中,我调用 m()。

我还尝试对每个方法分别使用 __declspec(dllexport),但静态成员仍然出现相同的链接错误。

如果我使用 dumpbin 检查 DLL(.lib),我可以看到符号已被导出。

例如,应用程序在链接时给出以下错误:

1>Main.obj : error LNK2001: unresolved external symbol "protected: static int CalcEngine::i_MatrixRow" (?i_MatrixRow@CalcEngine@@1HA)

但是.lib的dumpbin包含:

Version      : 0
  Machine      : 14C (x86)
  TimeDateStamp: 4BA3611A Fri Mar 19 17:03:46 2010
  SizeOfData   : 0000002C
  DLL name     : CalcEngine.dll
  Symbol name  : ?i_MatrixRow@CalcEngine@@1HA (protected: static int CalcEngine::i_MatrixRow)
  Type         : data
  Name type    : name
  Hint         : 31
  Name         : ?i_MatrixRow@CalcEngine@@1HA

我不知道如何解决这个问题。我究竟做错了什么?我该如何克服这些错误?

附:该代码最初是为 Linux 开发的,.so/binary 组合可以正常工作

编辑:在给定的情况下,应用程序不直接引用静态变量,但该方法是内联的,因为它位于标头中。我能够通过将方法移动到 .cpp 文件来解决链接错误。

【问题讨论】:

  • 我的第一反应是责备名称损坏,但是从检查垃圾箱来看,似乎损坏的名称匹配。只是为了确定,您是否使用相同的编译器版本编译两者?
  • 是的。我不确定编译器版本,但我使用 Visual C++ 2008 Express 编译 DLL 和应用程序

标签: c++ windows linker-errors dllexport


【解决方案1】:

在 cprogramming.com 的 this 线程中,建议静态变量是 dll 的本地变量,而不是导出的。

下面的讨论摘要

调用应用程序中的代码不能直接访问静态成员,只能通过dll中类的成员函数访问。然而,有几个 inline 函数访问静态成员。这些函数将被内联扩展到调用应用程序代码中,使调用应用程序直接访问静态成员。这将违反上面提到的发现,即静态变量是 dll 的本地变量,不能从调用应用程序中引用。

【讨论】:

  • 是的,我遇到了(我已经尝试解决这个问题半天了)。但是给定的解决方案不适用于我的情况,因为我没有在我的应用程序中使用 dll 中定义的静态成员。它们是隐藏的,仅由本地方法使用。
  • 是否有任何内联方法访问静态成员?在这种情况下,内联扩展代码将被放入您的应用程序中,直接访问静态成员。
  • 尝试将所有使用静态成员的方法体移动到 .cpp 文件中,这些文件被编译到 dll 中并且不能在应用程序中内联,这可能会成功。
  • 经过大量工作,我能够编译可执行文件。我不得不将很多方法从 .h 移动到 .cpp 文件。这不能解释为什么导出的符号被报告为未解析。
  • 我有点犹豫是否将其设置为答案,因为在链接的答案中,显示静态成员的符号“可以”导出。为应用程序和 dll 创建了两个实例,但符号未给出为“未解析”,这就是我的情况。请更正帖子,我会接受的。
【解决方案2】:

我的猜测是使用 DLL 的类应该在标题中看到 dllimport 而不是 dllexport。如果我是正确的,这通常可以通过定义一个预处理器宏来实现:

#ifdef EXPORTING
#define DECLSPEC __declspec(dllexport)
#else
#define DECLSPEC __declspec(dllimport)
#endif

然后在类声明中使用它:

class DECLSPEC Test{
protected:
    static int d;
public:
    static void m(){}
}

因此,在 Test.cpp(或在您的 DLL 项目中有意义的任何地方)中,您可以指定要导出,以便使用 dllexport 导出:

#define EXPORTING
#include "Test.h"

int Test::d;

而没有定义 EXPORTING 的其他项目将看到 dllimport

有意义吗?

【讨论】:

  • 我认为 dllimport 是可选的。我在某处读到它是一种机制,可以帮助链接器和编译器执行更好的优化,从而提高性能。如果我错了,请纠正我,因为我在这方面的经验有限
  • 是的,我还读到 dllimport 是可选的。我的理解是你可以省略它,但是对于导入dll的应用程序,dllexport不应该在那里:否则编译器认为它是由应用程序导出的,查找相应的定义,它不存在,这会产生错误消息.
  • 如果导出的成员将被更高级别的程序集 (dll/exe) 使用,我认为 dllimport 不是可选的,您将收到链接器错误。我刚刚在我的一个项目中进行了实验,因为我也需要导出一个静态公共成员,它是一个字符串。
【解决方案3】:

对于 Windows DLL,__declspec(dllexport)__declspec(dllimport) 之间存在特定区别,编译 DLL 时应使用 dllexport,编译链接到此 DLL 的程序时应使用 dllimport。定义它的标准方法是使用宏。

以下是visual studio示例:

// The following ifdef block is the standard way of creating macros which make exporting 
// from a DLL simpler. All files within this DLL are compiled with the DLL_EXPORTS
// symbol defined on the command line. this symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see 
// DLL_API functions as being imported from a DLL, whereas this DLL sees symbols
// defined with this macro as being exported.
#ifdef DLL_EXPORTS
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif

【讨论】:

    【解决方案4】:

    尽管有摘要,但可以从 DLL 导出静态数据。但是,Visual Studio DLL 项目提供的标准宏存在一个问题:

    #ifdef DLL_EXPORTS
    #define DLL_API __declspec(dllexport)
    #else
    #define DLL_API __declspec(dllimport)
    #endif
    

    如果您有多个 DLL 从一个 DLL 调用代码到另一个 DLL 或在 EXE 和 DLL 之间调用代码,则此宏将出现问题,因为每个标头都将被导出。需要处理 __declspec 的独特宏。处理这个问题最安全的方法如下:

    #ifdef MYPROJECT_DLL_EXPORTS
         #define MYPROJECT_API __declspec(dllexport)
    #else
         #define MYPROJECT_API __declspec(dllimport)
    #endif
    

    然后只在 DLL 项目的编译器预处理器选项中定义 MYPROJECT_API。 在您的标头代码中:

    struct/class MYPROJECT_API myclass {
       static int counter;
    };
    

    在 .cpp 文件中:

    int myclass::counter = 0;
    

    【讨论】:

      【解决方案5】:

      在 c++17 中使用内联 static inline std::string static_variable

      【讨论】: