Just started doing some coding for a very simple automated test framework for internal use. (I know there are hundreds of them out there - really good ones too, but at the moment that's not interesting, so don't point me to any of those, please ;)) I then came across the following problem which I can't explain, thus asking for your help.
I have the following code as part of a DLL:
(The code is barely an embryo and took me <2 minutes to write, so it's logic, structure - nothing - is refined, in any way yet.)
h-file:
#pragma once
#ifdef __DLL__ // Defined in DLL-project
#define DLLEXPORT __declspec( dllexport )
#else
#define DLLEXPORT
#endif
class DLLEXPORT AutoTest
{
public:
enum eTestID {TESTID_SomeFunction};
AutoTest(eTestID id, LPVOID lpData)
{
if(sm_bTestsActive)
ExecTest(id, lpData);
}
void ActivateTests();
private:
static void ExecTest(eTestID id, LPVOID lpData)
{
}
static BOOL sm_bTestsActive;
};
cpp-file:
#include "StdAfx.h"
#include "AutoTest.hpp"
BOOL AutoTest::sm_bTestsActive = FALSE;
void AutoTest::ActivateTests()
{
sm_bTestsActive=TRUE;
}
This compiles just fine and the DLL gets generated.
Here's my problem though - when instantiating the class with:
AutoTest(AutoTest::TESTID_SomeFunction, &SomeData);
from the main application, the linker fails with
error LNK2001: unresolved external symbol "private: static int AutoTest::sm_bTestsActive" (?sm_bTestsActive#AutoTest##0HA)
<2 minutes to write - now going on 5 hours to understand why it fails!!! :O
Here's what interesting - if I move the constructor to the cpp-file (not inlined) it works just fine!?!?
Here's that code:
h-file:
#pragma once
#ifdef __DLL__ // Defined in DLL-project
#define DLLEXPORT __declspec( dllexport )
#else
#define DLLEXPORT
#endif
class DLLEXPORT AutoTest
{
public:
enum eTestID {FK3059};
AutoTest(eTestID id, LPVOID lpData);
void ActivateTests();
private:
static void ExecTest(eTestID id, LPVOID lpData)
{
}
static BOOL sm_bTestsActive;
};
cpp-file:
#include "StdAfx.h"
#include "AutoTest.hpp"
BOOL AutoTest::sm_bTestsActive = FALSE;
AutoTest::AutoTest(eTestID id, LPVOID lpData)
{
if(sm_bTestsActive)
ExecTest(id, lpData);
}
void AutoTest::ActivateTests()
{
sm_bTestsActive=TRUE;
}
(I've made some minor edits in the code after pasting, so there may or may not be simple syntax errors.)
Also, if I remove the reference to the static member from the inline versions constructor, it works fine.
Any ideas as to why the inline version won't work?
Pay attention to your definition of DLLEXPORT.
Make sure that it is properly expanded to __declspec(dllexport) when building the DLL, or __declspec(dllimport) when building the client.
I'd suggest using a macro with a more specific name than the generic DLLEXPORT (to avoid conflicts with other macros with the same name).
Having static data members accessed from inline member functions works fine for me (tested with VS2013).
Minimal repro:
Create a Visual Studio solution with an empty DLL project and an empty console application project.
Inside the DLL project add two files:
DllClass.h:
#pragma once
#ifndef TEST_DLL_CLASS
#define TEST_DLL_CLASS __declspec(dllimport)
#endif
class TEST_DLL_CLASS DllClass
{
public:
DllClass();
int GetMember() const
{
return m_data1;
}
static int GetStaticMember()
{
return sm_data2;
}
private:
int m_data1;
static int sm_data2;
};
DllClass.cpp:
#define TEST_DLL_CLASS __declspec(dllexport)
#include "DllClass.h"
int DllClass::sm_data2 = 2;
DllClass::DllClass()
: m_data1(1)
{
}
Inside the console app project, add one file:
Test.cpp:
#include "..\DllTestClass\DllClass.h"
#include <iostream>
using namespace std;
#pragma comment(lib, "DllTestClass")
int main()
{
DllClass dllClass;
cout << dllClass.GetMember() << endl;
cout << DllClass::GetStaticMember() << endl;
}
Make sure that when building the console test app, the linker can find the DLL .lib (DllTestClass.lib) file.
For that purpose, you can navigate the console app's project properties, going to:
Project Properties | Linker | Additional Library Directories
and add $(OutDir) to the additional library directories, making it:
$(OutDir);%(AdditionalLibraryDirectories)
Builds and works correctly for me.
Should be:
#ifdef __DLL__ // Defined in DLL-project
#define DLLEXPORT __declspec( dllexport )
#else
#define DLLEXPORT __declspec( dllimport )
#endif
You can declare C++ classes with the dllimport or dllexport attribute. These forms imply that the entire class is imported or exported. Classes exported this way are called exportable classes.
More information in the documentation.
Related
Basically I have a dll project in visual studio. I'm linking this dll project to a second project successfully but as soon as I try to use extern variables things go wrong. I've seperated my extern variables into a single header and source file from everything else so I can isolate the problem and potential solutions. I've scowered the internet for hours now and I feel like I've tried everything. I am beginning to think it might be a compile flag? Anyway, heres my code.
macros.h
#pragma once
// dll management
#ifdef TOAST_EXPORT
#ifdef _MSC_VER
#define TAPI __declspec(dllexport)
#else
#define TAPI __attribute__((visibility("default")))
#endif
#else
#ifdef _MSC_VER
#define TAPI __declspec(dllimport)
#else
#define TAPI
#endif
#endif
globals.h
#pragma once
#define TOAST_EXPORT
#include "macros.h"
namespace toast
{
TAPI extern const char c;
}
globals.c
Note that I tried this without const and made no assignment here
#pragma once
#include "globals.h"
namespace toast
{
const char c = 'a';
}
main.c (from the project that compiles to an exe)
#include <globals.h>
int main()
{
char c = toast::c;
return 0;
}
So far I've tried making a lot of subtle changes like reordering extern and const and such. I've also done it both with just extern and just const. Still produces the same unresolved external symbol "char const toast::c" error. Keep in mind that I can create instances of classes and call their methods from the dll successfully and thats with things like class TAPI logger... and such.
In visual studio I've set configuration type as .dll instead of .exe and because of that sometimes I need to use __declspec(dllexport) or __declspec(dllimport) . So I've created macros for them inside header file called "Core"
#pragma once
#ifdef B5_PLATFORM_WINDOWS
#ifdef B5_BUILD_DLL
#define B5_API __declspec(dllexport)
#else
#define B5_API __declspec(dllimport)
#endif // B5_BUILD_DLL
#else
#error Bos5 only supports Windows!
#endif // B5_PLATFORM_WINDOWS
I have a class "Application" inside of my namespace "Bos5" which uses BS_API
#pragma once
#include "Core.h"
namespace Bos5 {
class B5_API Application
{
public:
Application();
~Application();
void Run();
};
}
everything works fine inside this project but when I reference this to another project for some reason visual studio thinks B5_API is a class and "Application" isn't. therefore code below doesn't compile saying namespace Bos5 doesn't have struct or class called "Application"
#include <FinalBos5.h>
class Sandbox : public Bos5::Application
{
public:
Sandbox(){}
~Sandbox(){}
};
int main() {
}
I think this image can better explain what I'm saying
Okay I've fixed it. turns out problem was completely different thing. My preprocessor didn't save some of the definitions so in second project B5_API wasn't getting defined
I'm currently developing a game that comes in two parts: the engine is in a .dll and the actual game code is part of an .exe. The engine, among other header files, contains one that manages all components, ranging from Win32 specific objects and pointers to D3D11. These components are included in a class that allows them to be accessed globally via a function that returns a reference to it. Furthermore, for every item it manages there are two functions that allow the component to be set or to be returned. I also added a thread protection using a critical section.
So, in State.h I have:
#if defined(NF3D_NONCLIENT_BUILD)
#define NF3D_API __declspec(dllexport)
#else
#if defined(__MINGW32__)
#define NF3D_API NF3D_API
#else
#define NF3D_API __declspec(dllimport)
#endif
#endif
...
#endif
...
extern NF3D_API CRITICAL_SECTION CriticalSection;
class NF3D_API NF3DLock
{
public:
#pragma prefast( suppress:26166, "Thread safeness is enabeled." )
FORCEINLINE _Acquires_lock_(CriticalSection) NF3DLock(void) { EnterCriticalSection(&CriticalSection); }
#pragma prefast( suppress:26165, "Thread safeness is enabeled." )
FORCEINLINE _Releases_lock_(CriticalSection) ~NF3DLock(void) { LeaveCriticalSection(&CriticalSection); }
};
class NF3D_API STATE
{
public:
...
inline void SetMember(int val) { NF3DLock Lock; Elements.Member = val; }
inline int GetMember(void) { NF3DLock Lock; return Elements.Member; }
private:
struct ELEMENTS
{
int Member;
} Elements;
}
FORCEINLINE NF3D_API STATE* NF3DGetEngineState(void);
In State.cpp I initialise and delete the critical section but also:
CRITICAL_SECTION CriticalSection;
...
FORCEINLINE NF3D_API STATE* NF3DGetEngineState(void)
{
static STATE s_State;
return &s_State;
}
Calling 'NF3DGetEngineState' inside the .dll causes no problems and the compilation runs perfectly, but if I use this function outside the engine, inside the application I get a linker error in the .exe:
2>Main.obj : error LNK2001: unresolved external symbol "struct _RTL_CRITICAL_SECTION NewFrontiers3D::CriticalSection" (?CriticalSection#NewFrontiers3D##3U_RTL_CRITICAL_SECTION##A)
The engine is included in a namespace called 'NewFrontiers3D'.
This error intrigued me even more when I declared 'extern NF3D_API CRITICAL_SECTION CriticalSection' as static, as it compiled fine but gave an access violation exception when entering the critical section in the constructor of NF3DLock. Also, if I remove 'EnterCriticalSection' and LeaveCriticalSection' the linker errors disappear. I don't know what is happening and why is it happening and that's why I'm addressing this question to anybody who might be able to help me.
Update:
From the line:
#define NF3D_API __declspec(dllexport)
it looks like you are always exporting, and never importing (which you should do when the header is included from external projects).
For example, compile your code using defining NM3D_API_EXPORTS macro (if I am not mistaken: -d option.
And, then, in the header define NM3D_API in the following way:
#ifdef NM3D_API_EXPORTS
#define NM3D_API __declspec(dllexport)
#else
#define NM3D_API __declspec(dllimport)
#endif
Original Post:
Include's work in a very dumb way. They take whole contents of the file you are including, and paste them over the include statement.
This is why, when you are trying to include this header to a different project, that project doesn't know about your declaration in previous .dll. Since it was done in a different file, about which your other project knows nothing about.
I'm trying to call a statically-linked static method of a C++ class, but I'm getting the VS linker error LNK2019, "unresolved external symbol". Here's the library source:
// in header file
#define DllExport __declspec (dllexport)
class MyClass{
public:
DllExport
static HWND WINAPI myFunc();
};
// in cpp file
DllExport
HWND WINAPI MyClass::myFunc(){ /* create a GUI window that has instance of MyClass set as its property (using ::SetProp) */ }
myFunc is to serve as an entry point for creating objects of MyClass, which resides hidden in the library. Only such static functions can be used to influence the functionality of a MyClass instance (by providing the corresponding HWND).
Here's the library consumer:
#define DllImport __declspec(dllimport)
DllImport
HWND WINAPI myFunc();
...
int main(){
HWND hWnd=myFunc();
... // work with the window and attached MyClass instance
}
(I believe) all file linkages are set correctly - originally, myFunc was designed as a standalone function and all worked just fine. I suspect it must be some calling convetion mismatch that makes the linker produce the error on myFunc.
Read through multiple articles on this topic, namely
http://www.codeproject.com/Articles/28969/HowTo-Export-C-classes-from-a-DLL
and
https://msdn.microsoft.com/en-us/library/3y1sfaz2.aspx
but they didn't solve my problem.
Thanks for a suggestion!
Since your goal is to create static library, the first thing we want to do is eliminate any mention of dllexport/dllimport. Such specifiers are only used when you actually create a DLL project.
So for lib.h, we only need this (with some include guards added for good measure):
// lib.h
#ifndef LIB_H
#define LIB_H
class MyClass{
public:
static void myFunc();
};
#endif
The WINAPI specification is also unnecessary since you're the one calling the method and can just use the default calling convention without ABI issues (though if you do want to use WINAPI anyway, then you need to include <windows.h> in your header file).
For lib.cpp, we only need this:
// lib.cpp
#include <Windows.h>
#include "lib.h"
void MyClass::myFunc(){
::MessageBox(0,"myFunc call!",NULL,0);
}
For main.cpp in your app project, we only need this:
// main.cpp
#include <iostream>
#include <D:\staticlink\lib\lib.h>
int main(){
std::cout << "ahoj";
MyClass::myFunc();
char buf[10];
std::cin >> buf;
return 0;
}
I'd recommend configuring your include paths to find lib.h through your project settings instead of using absolute paths in your source code, but perhaps you can do that later after you get everything working.
After that, if a problem remains, the only thing you should need to ensure is that your app project is linking against lib.lib properly (linker settings).
Your import header file should look more like:
#define DllApi __declspec (dllexport)
class MyClass{
public:
DllApi
static HWND WINAPI myFunc();
};
I have a base class (QIndicator) and I want to implement derived classes in DLLs. The DLL project in Visual Studio 2012 for a sample derived class has the following code:
header file with base class
#ifndef _DLL_COMMON_INDICATOR_
#define _DLL_COMMON_INDICATOR_
// define the DLL storage specifier macro
#if defined DLL_EXPORT
#define DECLDIR __declspec(dllexport)
#else
#define DECLDIR __declspec(dllimport)
#endif
class QIndicator
{
private:
int x;
int y;
};
extern "C"
{
// declare the factory function for exporting a pointer to QIndicator
DECLDIR QIndicator * __stdcall getIndicatorPtr(void);
}
#endif
source file with derived class
#define DLL_EXPORT
#include "indicator.h"
class QIndicatorDer : public QIndicator
{
public:
QIndicatorDer (void) : QIndicator(){};
~QIndicatorDer (void){};
private:
// list of QIndicatorDer parameters
int x2;
int y2;
};
extern "C"
{
DECLDIR QIndicator * __stdcall getIndicatorPtr(void)
{
return new QIndicatorDer();
};
}
The problem I have is that upon successful build, the produced DLL file does not contain the exported getIndicatorPtr function (as shown by DependencyWalker). I checked whether the dllexport keyword gets propagated properly into the declaration of getIndicatorPtr and it does.
Another interesting problem is that I already have another derived class like this, in another DLL project, that I created some months ago. This older project is basically the same and everything works well there. I checked all properties of both the old and the current projects, and they seem identical. So I ran out of ideas, why I can't get getIndicatorPtr to export.
Any help is much appreciated,
Daniel
That's because it's not being exported. Why?
__declspec specifier should only be placed in the declaration of a function, not it's definition. Also, avoid something like #define DLL_EXPORT. Preprocessor definitions should either defined in project properties (MSVC) or command line option (-D in GCC, for example).
Look at you code:
Header
extern "C"
{
DECLDIR QIndicator * __stdcall getIndicatorPtr(void);
}
When compiler parses this header, is sees DECLDIR as dllimport (because you define DLL_EXPORT in .cpp). Then in .cpp, it suddenly appears as dllexport. Which one is used? The first one.
So, leave your header (it's fine), but change your source:
//#define DLL_EXPORT -> remove this!
#include "indicator.h"
class QIndicatorDer : public QIndicator
{
//...
};
extern "C"
{
/* DECLDIR -> and this! */ QIndicator * __stdcall getIndicatorPtr(void)
{
return new QIndicatorDer();
};
}
Then, go to project properties (I assume you use Visual Studio) and then C/C++ -> Preprocessor -> Preprocessor Definitions and add there DLL_EXPORT=1.
That should work.