Link static member from dll

6532e3e5e09db6f966770fdf86c03345
0
hellhound_01 104 Dec 16, 2011 at 21:33 c++

Hi,

I’ve a strange problem with linking a static member from a dll using Visual Studio Express 2010. On Linux platform
and MSYS/MinGW (GCC) this failure doesn’t occurs.

I’ve a Math library using an export macro explicitly for this library module:

#ifdef WIN32
  #ifdef MATH_LIBRARY_EXPORT
    #define MATH_LIBRARY_API __declspec(dllexport)
  #else
    #define MATH_LIBRARY_API __declspec(dllimport)
  #endif
#else
  //define empty values for linux OS
  #define MATH_LIBRARY_API
#endif

And this is a snipped of my Vector class i export with static members:

ifndef BINREV_VECTOR_H__
#define BINREV_VECTOR_H__

// include common header with dll import/export macro
#include <brMath/brCommons.h>

namespace binrev{
namespace brMath{

class MATH_LIBRARY_API brVector3f
{  
public:
   float m_fX, m_fY, m_fZ;

   brVector3f(void);
   brVector3f(float x, float y, float z);

   ...

public:
   static const brVector3f ZERO;
   static const brVector3f NEGATIVE_UNIT_X;
   ...
};

And the cpp module:

// Ensure that the dll hader will be exported
#define MATH_LIBRARY_EXPORT
#include <brMath/brVector3f.h>

namespace binrev{
namespace brMath{

const brVector3f brVector3f::ZERO(0.0f, 0.0f, 0.0f);
const brVector3f brVector3f::UNIT_X(1.0f, 0.0f, 0.0f);

...

In my Graphics module (also an dll with an different explicit
export macro) using this Math dll i try to access one of those
static members:

#include <brMath/brVector3f.h>

brMath::brVector3f brCamera::getDirection(void)
{
    return  m_orientation.rotate(brMath::brVector3f::NEGATIVE_UNIT_Z);
}

On the other platforms anything works well, but with MVSE 2010 i got
a linker failure:

1>------ Erstellen gestartet: Projekt: ZERO_CHECK, Konfiguration: Debug Win32 ------
2>------ Erstellen gestartet: Projekt: brGraphics, Konfiguration: Debug Win32 ------
2>  brCamera.cpp
2>brCamera.obj : error LNK2001: Nicht aufgelöstes externes Symbol ""public: static class binrev::brMath::brVector3f const binrev::brMath::brVector3f::NEGATIVE_UNIT_Z" (?NEGATIVE_UNIT_Z@brVector3f@brMath@binrev@@2V123@B)".
2>C:\binrev\repository\binrevengine\modules\brGraphics\trunk\bin\brGraphics.dll : fatal error LNK1120: 1 nicht aufgelöste externe Verweise.
========== Erstellen: 1 erfolgreich, Fehler bei 1, 0 aktuell, 0 übersprungen ==========

I’m not a friend of MVSE and this are my first tries to get our code runable
with MVSE. I’ve copied the brMath.lib and include files to the VC subfolder
of the MVSE home directory and also copy the brMath.dll to the output
directory where the brGraphics.dll should be build. I also imported the dll
as external file to the Visual Studio Solution explorer …

Now i running out of ideas what could be wrong.
I’m grateful for any help or hint.

12 Replies

Please log in or register to post a reply.

A8433b04cb41dd57113740b779f61acb
0
Reedbeta 168 Dec 16, 2011 at 22:46

You need to tell Visual Studio that the .lib needs to be linked into the project. If the projects are both being built within the same solution, then you should just need to add a reference from brGraphics to brMath. If you right-click on brGraphics in the solution explorer and choose References, you can add one. This should handle linking one lib to the other.

If the brMath project isn’t in the same solution, the typical way to do this is to go into the Project Settings, find the Configuration Properties -> Linker -> Inputs tab, and add the .lib’s filename in Additional Dependencies. Don’t forget to do this for each configuration (Debug/Release). Also add the .lib’s directory path to Linker -> General -> Additional Library Directories if necessary. You do not need to put any files in the MVSE directories, nor add the .dll to the solution explorer.

6532e3e5e09db6f966770fdf86c03345
0
hellhound_01 104 Dec 16, 2011 at 23:58

Thanks for the great introducion Redbeeta. But this was not the failure. The brMath.lib was already set as Additional Dependency to the linker only the path to the library was not set. The same failure still exists…

6532e3e5e09db6f966770fdf86c03345
0
hellhound_01 104 Dec 17, 2011 at 21:33

I’ve added the actual linker command for review:

/OUT:"C:\binrev\repository\binrevengine\modules\brGraphics\trunk\bin\brGraphics.dll" /INCREMENTAL /NOLOGO
/LIBPATH:"C:\binrev\repository\binrevengine\modules\brMath\trunk\lib\Debug" /DLL "kernel32.lib" "user32.lib" "gdi32.lib"
"winspool.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "comdlg32.lib" "advapi32.lib" "brCore.lib" "brMath.lib"
"c:\binrev\development\vs2010\VC\lib\libboost_signals-vc100-mt-gd-1_47.lib" "c:\binrev\development\vs2010\VC\lib\libboost_system-vc100-mt-gd-1_47.lib"
/MANIFEST /ManifestFile:"brGraphics.dir\Debug\brGraphics.dll.intermediate.manifest" /ALLOWISOLATION
/MANIFESTUAC:"level='asInvoker' uiAccess='false'" /DEBUG /PDB:"C:\binrev\repository\binrevengine\modules\brGraphics\trunk\bin\brGraphics.pdb"
/SUBSYSTEM:CONSOLE /STACK:"10000000" /PGD:"C:\binrev\repository\binrevengine\modules\brGraphics\trunk\bin\brGraphics.pgd" /TLBID:1
/DYNAMICBASE /NXCOMPAT /IMPLIB:"C:/binrev/repository/binrevengine/modules/brGraphics/trunk/lib/Debug/brGraphics.lib" /MACHINE:X86 /ERRORREPORT:QUEUE

It seems too me that anything is linked correctly. When I remove the call of the const static dll member of brVector3f the build is successfull.
I think there must be a problem with the const static member. But why in hell only with MVSE ?

I couldn’t find any difference to this introducion: http://stackoverflow.com/questions/2479784/exporting-static-data-in-a-dll
So it normally should be working …

340bf64ac6abda6e40f7e860279823cb
0
_oisyn 101 Dec 21, 2011 at 10:57

Just created a basic test setup, exporting static class members in a DLL and importing them in another project works fine. What struck me as interesting is that your error doesn’t specify that brVector3f::NEGATIVE_Z is being marked as __declspec(dllimport).

In my DLL I have this basic class:

class __declspec(dllexport) Test
{
public:
    static int s_data;
};

int Test::s_data = 42;

In my main project, if I do this:

#include <iostream>


class Test   // yes, NO import!
{
public:
    static int s_data;
};

int main()
{
    std::cout << Test::s_data << std::endl;
}

I get:

error LNK2001: unresolved external symbol "public: static int Test::s_data" (?s_data@Test@@2HA)

This is similar to the error you’re getting.

When I change the class to

class __declspec(dllimport) Test   // let's import this time!
{
public:
    static int s_doesnotexist;    // but specify a non-existing member in the DLL
};

I get:

error LNK2001: unresolved external symbol "__declspec(dllimport) public: static int Test::s_doesnotexist" (__imp_?s_doesnotexist@Test@@2HA)

Note the “__declspec(dllimport)” in front of the identifier.
So clearly your missing a __declspec(dllimport) on your class in your main project.

I’ve copied the brMath.lib and include files to the VC subfolder
of the MVSE home directory and also copy the brMath.dll to the output
directory where the brGraphics.dll should be build. I also imported the dll
as external file to the Visual Studio Solution explorer …

It’s not very good practice this way. It’s best to stay clear of the standard VC++ directories, you NEVER EVER have to punt anything in those directories. Just create a solution with both your DLL project and your main project. In your main project, add a reference to your DLL project as ReedBeta suggested. That’s all you have to do, it will automatically build the appropriate .lib and link to it. For the headers, you can add the dir containing the headers of the DLL to the “Additional include directories” in your main project’s settings (Under C++ -> General, be sure to add the dir for all build configurations - see the dropdown in the top left corner of the properties dialog)

6532e3e5e09db6f966770fdf86c03345
0
hellhound_01 104 Dec 21, 2011 at 11:43

Thanks Oisyn for your test, but if I understood the usage of the import/export macro correctly the dllimport should be there. When I call the brVector3f header from my class which should use those constants (you call it main class), the macro should dynamically set the MATH_LIBRARY_API to dllimport, while MATH_LIBRARY_EXPORT is not defined there. Only on export when invoking the cpp module of my brVector3f class those flag is set to true, in that case the value should be dllexport …

I’ve tested the dependency walker and checked my brMath and brCore dll’s (could not test brGraphics, while not exist) those constants should be exported, if i understood the report output of this tool correctly:

E : c++
Ordinal:  241 (0x00F1)
Hint :  240(0x00F0)
Function:  cass binrev::brMath::brVector3f binrev::brMath::brVector3f::NEGATIVE_UNIT_X
Entry Point : 0x00030534

t’s not very good practice this way. It’s best to stay clear of the standard VC++ directories, you NEVER EVER have to punt anything in those directories. Just create a solution with both your DLL project and your main project. In your main project, add a reference to your DLL project as ReedBeta suggested ..

Thanks for this hint, I’ve fixed it. I’ve copied those files like I do it typically using MSYS/MinGW.

6532e3e5e09db6f966770fdf86c03345
0
hellhound_01 104 Dec 22, 2011 at 11:34

Following the instructions of Reedbeta and Oisyn I’ve implemented a simple TestExport
Dll project which could be downloaded here: https://sourceforge.net/projects/binrevengine/files/publications/

This project has the same problems as described. It’s impossible to access the static const
member from anywhere I include the DLL …

46407cc1bdfbd2db4f6e8876d74f990a
0
Kenneth_Gorking 101 Dec 22, 2011 at 17:51

I just checked the .dll, and the m_value is exported and available, so it is probably something else that’s wrong. Maybe you are using the EXPORT macro wrong? If it is also defined in projects where you want to import the class, that would certainly result in failure…

6532e3e5e09db6f966770fdf86c03345
0
hellhound_01 104 Dec 23, 2011 at 13:13

Thanks Kenneth for your check.

I think the macro is used correctly. If you look in the brCommon header of the brTestDLL project, you see that the dllexport is only defined if the TEST_DLL_LIBRARY_EXPORT constant flag is defined. This constant flag is only defined in the brTestDLL cpp module. In the main project (using the exported dll) is only the brTestDLL header included, by this reason the brCommon reference should use the dllimport in that case….

I’ve checked this project again on GCC, there is no problem. So I think there must be a problem with MVSE configuration …

340bf64ac6abda6e40f7e860279823cb
0
_oisyn 101 Dec 23, 2011 at 14:34

in brCommons, could you add a line of code to the part where you define MATH_LIBRARY_API as __declspec(dllimport)? Like so:

#ifdef WIN32
  #ifdef MATH_LIBRARY_EXPORT
        #define MATH_LIBRARY_API __declspec(dllexport)
  #else
        #define MATH_LIBRARY_API __declspec(dllimport)
        #error this error should be generated
  #endif
#else
  //define empty values for linux OS
  #define MATH_LIBRARY_API
#endif

Then, recompile your main project. If you don’t see the error, MATH_LIBRARY_API is obviously not defined as __declspec(dllimport)

6532e3e5e09db6f966770fdf86c03345
0
hellhound_01 104 Dec 23, 2011 at 17:04

Thanks Oisyn for the hint, this is the failure, the defintion for the import are not called. I’ve also added an pragma message to
show that the header is called, what it is … Now i doesn’t understand why MATH_LIBRARY_EXPORT is also defined in my
main project … I’ve searched the source and it is only once declared in the brTestDLL cpp module …

In my visual studio project the commands of the else part has some transparent glow. But why? And why only in MVSE?
Same header works well with GCC …

46407cc1bdfbd2db4f6e8876d74f990a
0
Kenneth_Gorking 101 Dec 23, 2011 at 17:45

Maybe it is defined in the solution? Go to the projects properties, then look under ‘C/C++ -> Preprocessor’ to see if its defined there. Also try looking under ‘C/C++ -> Command line’.

6532e3e5e09db6f966770fdf86c03345
0
hellhound_01 104 Dec 23, 2011 at 20:24

Ok problem solved! Thanks for your support guys!

There was a problem with the macro as Oisyn has supposed. I used the WIN32 constant for the ifdef defintion. I don’t know why, but this constant was sometimes not regard by my solutions. In brTestDLL export this constant was supported and the export definition used, in my main project, Win32 constant was not accepted but _WIN32 … After changing those constants to _WIN32 anything works fine … Now I’ve to recheck the GCC …