3

I need help access global functions across DLLs/main program. I have a class 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 application) (MAIN_DLL is predefined here)

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

When I access from the dll:

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

Thanks, Gasim

Gasim
  • 7,615
  • 14
  • 64
  • 131
  • 1
    This might be of interest for you: http://stackoverflow.com/questions/4911994/sharing-a-global-static-variable-between-a-process-and-dll – marcinj Dec 28 '11 at 10:23

2 Answers2

5

This is system dependent, but you'll have to ensure that the symbols in the DLL containing the definitions of the member functions and static member data correctly exports the symbols, and that the DLL using them correctly imports them. Under Linux, this means using the -E option when linking the executable (if the symbols are defined in the executable); under Windows, you usually have to use conditionally compiled compiler extensions, see __declspec; Microsoft compilers do not support DLL's in standard C++.

EDIT:

Here's an example that works on my system (VC 2010):

In 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

In A.cpp:

#include "A.h"

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

void
InA::setDest( std::ostream& dest )
{
    ourDest = &dest;
}

In main.cpp:

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

int
main()
{
    InA::setDest( std::cout );
    std::cout << InA::getStream() << std::endl;
    return 0;
}

Compiled and linked with:

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

As I understand it (I'm more a Unix person), all of the .cpp which become part of the dll should have /DDLL_A in the command line which invokes the compiler; none of the others should. In Visual Studios, this is usually achieved by using separate projects for each dll and each executable. In the properties for the project, there's an entry ConfigurationProperties→C/C++→Preprocessor→Preprocessor Definitions; just add DLL_A there (but only in the one project that generates A.ddl).

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • I have been trying to fix the code to get what you were saying. I got it to working! Thank you for the help! – Gasim Dec 28 '11 at 12:53
  • i have fixed the issue but I am getting LNK4049. I don't know what I am doing wrong. – Gasim Dec 28 '11 at 13:10
  • @Gasim without more information, nor do I. What are you doing exactly? (The usual solution is to define `DLLNAME_EXPORT` to `__declspec(dllexport)` when compiling code in the DLL `DLLNAME`, and to `__declspec(dllimport)` elsewhere. It sounds as if you've got it defined differently in two sources which are part of the same DLL.) – James Kanze Dec 28 '11 at 13:55
  • @Gasim I've edited in an example of something that works on my system (VC 2010). Maybe that will give you an idea what you're doing wrong. – James Kanze Dec 28 '11 at 14:22
  • I found the problem but still don't know how to solve it. I have a static library that takes care of the setters and getters. when I put __declspec(dllimport) on that one. It gives me the warning. I changed the code in my main program. If I remove it, Its says unresolved external symbol **system**, **filesystem**, **logger**. When I put the static definitions inside the static lib, it gives me error because the **system** becomes null. But the program works as charm, so I don't know why I am getting that error. I am gonna try to change the static lib into a dynamic lib. – Gasim Dec 28 '11 at 14:33
  • @Gasim It's not always obvious to mix static and dynamic linking. When dynamically linking, the static library should only be linked with one DLL (at least in the model I think you're using); but I'm not sure what the effects of `__declspec(dllexport)` or `__declspec(dllimport)` would be in a static library. At any rate, I would definitely **not** put the getters and the setters in a separate library than the static objects themselves. I don't know how to make that work. – James Kanze Dec 28 '11 at 18:48
  • @JamesKanze thanks for this response; please consider the question at http://stackoverflow.com/questions/24027333/combined-static-member-and-method – user1823664 Jun 04 '14 at 00:57
4

The problem is that your header file meant for compilation into a DLL contains code! So "main.exe" exectutes a local copy of the inline functions (such as Base::sys), but the actual implementation of "Base::setSystem" is compiled into the DLL. So when main invokes the "setSystem" call, it calls the Base::setSystem linked into the DLL. But when it compiles Base::sys, it sees that an inline implementation exists and uses that.

In other words, you have two copies of "Base" floating around. One that lives in the EXE and another that lives in the DLL. And the inline functions confuse the compiler and linker as to which version to invoke.

Don't put inline functions (or code for that matter) in a header file where the implementation is meant to live in a DLL.

Easy fix:

// 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

Right fix:

You really, really, really should not be attempting to export C++ classes from DLLs. It's allowed, but gets complicated really quick when you start inlining code in header files and changing interfaces in different versions of the DLL.

A better approach is to export a pure "C" library from a DLL. And don't expose the internals in header files. OR, if you do want to export C++ classes, do it with COM interfaces. Then all you are doing is putting an interface declaration into your header file.

selbie
  • 100,020
  • 15
  • 103
  • 173
  • I changed everything as you said and also created a static library because It won't compile (link errors) without putting these files in both projects. But it still doesn't work. My problem is, I am trying to have the static variables across the dlls. Main application sets up the static variables using the setters and all the dlls should have the "global" variables accessed through getters. Is it possible? – Gasim Dec 28 '11 at 10:43
  • It certainly is possible. What is the link error - it is possibly indicative of a real problem. You are properly exporting these functions from the DLL, right? Either with __dllexport attribute or a .DEF file. – selbie Dec 28 '11 at 11:18
  • Ok. I have a static library "Base.lib" that stores the Base class symbols. The "engine.exe" has to initialize the variables of the Base class using the setters. And, the DLL uses these variables using the getters. Getters and setters do not have to be exported but the static variables do have to. So, I have added `#pragma data_seg(".base_seg")` (i changed the source code for you to visually easily see it). It doesn't work though. – Gasim Dec 28 '11 at 11:46
  • @selbie Not sure about "right way" and "wrong way", but every place I've seen that uses Microsoft compilers exports C++ classes. It takes a bit of care (and some conditional compilation) to get it right, but it can be made to work. – James Kanze Dec 28 '11 at 13:57
  • 1
    @Gasim It's much easier to just export the entire class. – James Kanze Dec 28 '11 at 14:23
  • Oh, I wasn't thinking about that. I will do it now. – Gasim Dec 28 '11 at 14:35