跨 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 没有更多信息,我也没有。你到底在做什么? (通常的解决方案是在 DLLDLLNAME
中编译代码时将 DLLNAME_EXPORT
定义为 __declspec(dllexport)
,并在其他地方定义为 __declspec(dllimport)
。听起来好像您在两个源中定义不同,它们是相同的 DLL。)
@Gasim 我编辑了一个可以在我的系统上运行的示例(VC 2010)。也许这会让你知道你做错了什么。
我找到了问题,但仍然不知道如何解决。我有一个处理 setter 和 getter 的静态库。当我把 __declspec(dllimport) 放在那个上时。它给了我警告。我更改了主程序中的代码。如果我删除它,它会显示未解析的外部符号 system、filesystem、logger。当我将静态定义放在静态库中时,它给了我错误,因为 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 使用静态类变量/函数的主要内容,如果未能解决你的问题,请参考以下文章