跨 dll 使用静态类变量/函数

Posted

技术标签:

【中文标题】跨 dll 使用静态类变量/函数【英文标题】:use static class variable/function across dlls 【发布时间】:2011-12-28 09:55:26 【问题描述】:

我需要帮助来访问跨 DLL/主程序的全局函数。我有一个类 Base

Base.h

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


class Base 
private:
    DECLSPEC static Filesystem * filesystem;
    DECLSPEC static Logger * logger;
    DECLSPEC static System * system;

public:

    static void setFilesystem(Filesystem * filesystem_);
    static void setApplication(Application * application_);
    static void setLogger(Logger * logger_);
    static void setSystem(System * system_);

    static Filesystem * fs()  return filesystem; 
    static Logger * log()  return logger; 
    static System * sys()  return system; 

;

main.cpp(主应用程序)(此处预定义了 MAIN_DLL)

Filesystem * Base::filesystem = 0;
Logger * Base::logger = 0;
System * Base::system = 0;

当我从 dll 访问 时:

System * system = Base::sys();
if(system == 0) std::cout << "Error";

谢谢, 加西姆

【问题讨论】:

您可能会感兴趣:***.com/questions/4911994/… 【参考方案1】:

这取决于系统,但您必须确保包含成员函数和静态成员数据定义的 DLL 中的符号正确导出符号,并且使用它们的 DLL 正确导入它们。在 Linux 下,这意味着在链接可执行文件时使用-E 选项(如果在可执行文件中定义了符号);在 Windows 下,您通常必须使用条件编译的编译器扩展,请参阅__declspec; Microsoft 编译器不支持标准 C++ 中的 DLL。

编辑:

这是一个适用于我的系统 (VC 2010) 的示例:

在 A.h 中:

#ifndef A_h_20111228AYCcNClDUzvxOX7ua19Fb9y5
#define A_h_20111228AYCcNClDUzvxOX7ua19Fb9y5

#include <ostream>

#ifdef DLL_A
#define A_EXPORT __declspec(dllexport)
#else
#define A_EXPORT __declspec(dllimport)
#endif

class A_EXPORT InA

    static std::ostream* ourDest;
public:
    static void setDest( std::ostream& dest );
    static std::ostream* getStream()  return ourDest; 
;
#endif

在 A.cpp 中:

#include "A.h"

std::ostream* InA::ourDest = NULL;

void
InA::setDest( std::ostream& dest )

    ourDest = &dest;

在 main.cpp 中:

#include <iostream>
#include "A.h"

int
main()

    InA::setDest( std::cout );
    std::cout << InA::getStream() << std::endl;
    return 0;

编译和链接:

cl /EHs /LDd /DDLL_A A.cpp
cl /EHs /MDd main.cpp A.lib

据我了解(我更喜欢 Unix 人),所有的 .cpp 成为 dll 的一部分应该在命令行中有 /DDLL_A 调用编译器;其他人都不应该。在 Visual Studio 中, 这通常是通过为每个 dll 使用单独的项目来实现的,并且 每个可执行文件。在项目的属性中,有一个条目 配置属性→C/C++→预处理器→预处理器 定义;只需在此处添加DLL_A(但仅在一个项目中 生成A.ddl)。

【讨论】:

我一直在尝试修复代码以获取您所说的内容。我开始工作了!感谢您的帮助! 我已经解决了这个问题,但我得到了 LNK4049。我不知道我做错了什么。 @Gasim 没有更多信息,我也没有。你到底在做什么? (通常的解决方案是在 DLL DLLNAME 中编译代码时将 DLLNAME_EXPORT 定义为 __declspec(dllexport),并在其他地方定义为 __declspec(dllimport)。听起来好像您在两个源中定义不同,它们是相同的 DLL。) @Gasim 我编辑了一个可以在我的系统上运行的示例(VC 2010)。也许这会让你知道你做错了什么。 我找到了问题,但仍然不知道如何解决。我有一个处理 setter 和 getter 的静态库。当我把 __declspec(dllimport) 放在那个上时。它给了我警告。我更改了主程序中的代码。如果我删除它,它会显示未解析的外部符号 systemfilesystemlogger。当我将静态定义放在静态库中时,它给了我错误,因为 system 变为空。但是该程序很有魅力,所以我不知道为什么会出现这个错误。我将尝试将静态库更改为动态库。【参考方案2】:

问题是您的用于编译成 DLL 的头文件包含代码!所以“main.exe”执行内联函数(如Base::sys)的本地副本,但“Base::setSystem”的实际实现被编译到DLL中。因此,当 main 调用“setSystem”调用时,它会调用链接到 DLL 中的 Base::setSystem。但是当它编译 Base::sys 时,它发现存在一个内联实现并使用它。

换句话说,你有两个“基地”的副本漂浮在周围。一个存在于 EXE 中,另一个存在于 DLL 中。并且内联函数会混淆编译器和链接器来调用哪个版本。

不要将内联函数(或与此相关的代码)放在头文件中,因为该头文件的实现应该存在于 DLL 中。

简单修复:

// base.h (gets included by main)
class Base 
private:
static Filesystem * filesystem;
    static Logger * logger;
    static System * system;

public:

    static void setFilesystem(Filesystem * filesystem_);
    static void setApplication(Application * application_);
    static void setLogger(Logger * logger_);
    static void setSystem(System * system_);
    static Filesystem * fs();
    static Logger * log();
    static System * sys();
;

// base.cpp (gets compiled only within the DLL
System* Base::sys()

    return system;


// repeat "get" function for "log" and "fs" as well

正确修复:

您真的,真的,真的不应该尝试从 DLL 导出 C++ 类。这是允许的,但是当您开始在头文件中内联代码并在不同版本的 DLL 中更改接口时,它会变得非常复杂。

更好的方法是从 DLL 导出纯“C”库。并且不要在头文件中暴露内部结构。或者,如果您确实想导出 C++ 类,请使用 COM 接口。那么你所做的就是将接口声明放入你的头文件中。

【讨论】:

我按照你所说的改变了一切,还创建了一个静态库,因为如果不将这些文件放在两个项目中,它将无法编译(链接错误)。但它仍然不起作用。我的问题是,我试图在 dll 中使用静态变量。主应用程序使用 setter 设置静态变量,所有 dll 都应具有通过 getter 访问的“全局”变量。有可能吗? 这当然是可能的。什么是链接错误 - 它可能表明存在真正的问题。您正在正确地从 DLL 导出这些函数,对吗?使用 __dllexport 属性或 .DEF 文件。 好的。我有一个存储基类符号的静态库“Base.lib”。 “engine.exe”必须使用设置器初始化基类的变量。并且,DLL 通过 getter 使用这些变量。不必导出 getter 和 setter,但必须导出静态变量。所以,我添加了#pragma data_seg(".base_seg")(我更改了源代码以便您直观地看到它)。但它不起作用。 @selbie 不确定“正确方式”和“错误方式”,但我见过的每个使用 Microsoft 编译器的地方都导出 C++ 类。需要一点小心(和一些条件编译)才能让它正确,但它可以工作。 @Gasim 只导出整个类要容易得多。

以上是关于跨 dll 使用静态类变量/函数的主要内容,如果未能解决你的问题,请参考以下文章

如何导出类模板静态变量 DLL windows

跨 appdomain 的静态类变量

如何要求消费者使用类库来初始化静态变量

在 C++ DLL 中使用全局变量

类静态成员变量和静态成员函数的访问方式

java 静态变量生命周期(类生命周期)(转)