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.
Related
I have a project that is compiled into a library and declares a certain function to be implemented by the user of the library:
//To be defined by user
Application* CreateApplication();
When compiling the code into a shared library on Linux this works perfectly. Any user of the library can define an implementation for the declared function and it can be used inside the library. If the user of the library forgets to define an implementation, they will get an error pointing this out.
I'm now in the process of porting the library to Windows, where it is supposed to be compiled into a dll. However, I'm running into problems as the linker used by Visual Studio is complaining:
unresolved external symbol Application* __cdecl CreateApplication(void)
I tried adding the extern keyword to indicate that the definition of the function is somewhere else, but this didn't work.
Why can't I declare (but not define) a function in a dll like this? How should I fix my code so it works both on Linux and on Windows?
What you are attempting to do only works in a static library, it cannot work in a dynamic library like a DLL. For that, you will have to change the code to use a function pointer instead. The application that is using the DLL can pass in the address of the desired function from its own code, and the DLL can then assign that address to a variable that it uses as needed, eg:
HEADER:
#ifndef MYLIB_H
#ifndef MYLIB_H
#ifdef COMPILING_MY_LIB
#define MY_EXPORT __declspec(dllexport)
#else
#define MY_EXPORT __declspec(dllimport)
#endif
// declare Application as needed...
typedef Application (*lpCreateApplicationFunc)();
#ifdef __cplusplus
extern "C" {
#endif
MY_EXPORT void SetCreateApplicationFunc(lpCreateApplicationFunc func);
#ifdef __cplusplus
}
#endif
#endif
DLL:
#define COMPILING_MY_LIB
#include "MyLib.h"
//To be defined by user
lpCreateApplicationFunc CreateApplication = NULL;
void SetCreateApplicationFunc(lpCreateApplicationFunc func)
{
CreateApplication = func;
}
void doSomething()
{
Application *app = NULL;
if (CreateApplication)
app = (*CreateApplication)();
if (app)
{
...
}
}
EXE:
#include "MyLib.h"
Application MyCreateApplicationFunc()
{
...
}
// during startup, call this...
SetCreateApplicationFunc(&MyCreateApplicationFunc);
I'm using an external C library inside a C++ project.
The header contains a struct with a variable named class:
#ifdef __cplusplus
extern "C" {
#endif
struct something_t {
...
sometype class;
};
#ifdef __cplusplus
}
#endif
g++ does not like this and complains about "error: expected identifier before ';' token".
What options do I have?
I could rename class, but that's cumbersome and breaks upstream compatibility.
I could ask the upstream project to rename the variable, but that may be difficult.
I could redefine class in the header using the preprocessor: #define class class_ Are there any side effects?
Any other suggestions?
What's the best way to handle this situation?
Result: Based on the prevailing preference for option 2, I finally chose to initiate a renaming in the upstream library.
As others already mentioned in comments, the best option is to write another C API layer around that stuff, that uses the other API only internally.
Anything related to this offending struct definition should be exported through opaque pointers only.
In C++ you can use the cleaned up C-API then.
Here's a small sketch:
ThirdParty.h (contains offending code to compile with c++)
#ifdef __cplusplus
extern "C" {
#endif
struct something_t {
...
sometype class;
};
struct something_t* CreateSomething(); // Does memory allocation and initialization
void DoSomething(struct something_t* something);
#ifdef __cplusplus
}
#endif
MyApiWrapper.h
#ifdef __cplusplus
extern "C" {
#endif
typedef void* psomething_t;
struct psomething_t MyCreateSomething(); // Does memory allocation and initialization
void MyDoSomething(psomething_t something);
#ifdef __cplusplus
}
#endif
MyApiWrapper.c
#include "ThirdParty.h"
struct psomething_t MyCreateSomething() {
psomething_t psomething = (psomething_t)CreateSomething();
return psomething;
}
void MyDoSomething(psomething_t something) {
DoSomething((struct something_t*)psomething);
}
Regarding your considered solutions
I could ask the upstream project to rename the variable, but that may be difficult
You certainly should report that bug to let them know. If it's a git-hub hosted project prepare a pull request.
Anyways be prepared that they might not be responsive timely, and you should always have the above mentioned "plan B". It will work regardless ...
I could redefine class in the header using the preprocessor: #define class class_ Are there any side effects?
It could be a viable way, if any place where this particular symbol (class) appears is plain c code and no other parts of the 3rd party c code (e.g. as library) depends on that symbol (which is unlikely).
Context
Im working on a project designed to send certain commands to a device. Each device can be interfaced with a dll (e.g. deviceADll.h, deviceBDll.h) and the Dll's were not programmed by me, nor can I modify them in any way. I am in charge of integrating DeviceB to the project, with minimal changes to the structure of the project. I know the structure may not be optimal and/or well designed, so I am willing to take suggestion concerning that matter as a last resort solution.
Since the devices are very similar, all Dll functions have the same name, and often the same prototype.
Also because of this, I made a parent class (Device_ts.h), from which DeviceA_ts.h and DeviceB_ts.h inherit (I also have a factory class for the Devices, but I don't think that it's relevant to my problem).
Problem
The problem occurs when I try to include both Dlls: the project compiles, but I get a
Warning 60 warning LNK4006: Connect#12 already defined in DeviceA.lib(DeviceA.dll); second definition ignored C:\project_path\DeviceB.lib(DeviceB.dll) Project_Name
followed by a
Warning 61 warning LNK4006: __imp__Connect#12 already defined in DeviceA.lib(DeviceA.dll); second definition ignored C:\project_path\DeviceB.lib(DeviceB.dll) Project_Name
and a
Warning 62 warning LNK4221: This object file does not define any previously undefined public symbols, so it will not be used by any link operation that consumes this library C:\project_path\DeviceB.lib(DeviceB.dll) Project_Name
Has anyone experienced a similar situation? Should I ignore those warning or will I not be able to call DeviceB.h functions since their definitions are ignored?
I am using Visual Studio 2010, the Device_ts.h library I am writing is a static library and all the project's parameters (e.g. /MD, include directories, dependencies, MFC, etc) are set properly from what I found in my research for this problem.
Code
The include and code looks like this (I will only show one of the functions that cause the warning since I get the same error on 50 functions):
DeviceADll.h
#ifndef DEVICEA_H__
#define DEVICEA_H__
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
namespace DeviceA
{
// some struct definition that don't cause the linker warnings
//...
// function definitions
extern "C" HANDLE PASCAL EXPORT Connect( HANDLE h_devA, const char *ip);
// ...
} // namespace DeviceA
DeviceBDll.h
#ifndef DEVICEB_H__
#define DEVICEB_H__
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
namespace DeviceB
{
// some struct definition that don't cause the linker warnings
//...
// function definitions
extern "C" HANDLE PASCAL EXPORT Connect( HANDLE h_devB, const char *ip);
// ...
} // namespace DeviceB
Device_ts.h
#ifndef DEVICE_FCT_H_
#define DEVICE_FCT_H_
#ifndef EXPORT
#define EXPORT
#endif
#if _MSC_VER > 1000
#pragma once
#endif
#include "DeviceADll.h"
#include "DeviceBDll.h"
class CDevice {
public:
virtual BOOL Connect(char *ip_addr) = 0;
};
#endif DEVICE_FCT_H_
This is a good use-case for manual DLL loading, using LoadLibrary() and GetProcAddress().
You'll have to manage a function pointer for each function looked up this way, which is a bit of a pain, but bypassing the OS's dll loading gives you a lot of flexibility.
Also note that you do not need to link against the DLL when using this method, the dll binding is 100% runtime, and the linker is not involved at all.
Here's an example:
typedef void (*connect_fn)(HANDLE, const char*);
connect_fn connect_a;
connect_fn connect_b;
int main()
{
HINSTANCE dll_a = LoadLibrary("path_to_dll_a.dll");
HINSTANCE dll_b = LoadLibrary("path_to_dll_b.dll");
if (!dll_a || !dll_b) {
return 1;
}
connect_a = (connect_fn)GetProcAddress(dll_a , "Connect");
connect_b = (connect_fn)GetProcAddress(dll_b , "Connect");
// connect_a and connect_b can now be used.
return 0;
}
Edit: Basically, I suggest you treat the device DLLs as plugins, rather than dynamic libraries.
We are working on two C++ code base, let's call it A and B, the A is an build as an library, and distribute the header files .h and .a file to B.
Let's say there is Lock.h file in A as following:
// Lock.h in code base A
class Lock {
... ...
#ifdef TRACK_THREAD_OWNER_FOR_DEBUG
virtual int GetLockOwner();
#endif
... ...
private:
CriticalSection section;
#ifdef TRACK_THREAD_OWNER_FOR_DEBUG
int threadOwner;
#endif
};
// Caller.cc in code base B
#include "xxx/xxx/Lock.h"
Lock lockObject;
lockObject.Lock();
In code base A, we by default will enable TRACK_THREAD_OWNER_FOR_DEBUG and may change it just before final release day.
We hit some hard bug because TRACK_THREAD_OWNER_FOR_DEBUG are different in A and B, and cause memory corruption because the sizeof(Lock) is different in two library.
So how to protect from this error? Can we trigger an compiler error when build the caller.cc file if the build macro TRACK_THREAD_OWNER_FOR_DEBUG is different in two project?
It is not possible to make this into compiler error, however it should be possible to make this into reasonably clear linker error by exporting some symbol which name depends on the currently defined macros. For example using static guard variable:
// Foo.hpp - library header file
#pragma once
class Foo
{
public: Foo();
#ifdef IMPORTANT_CONDITION
int m_field;
#endif
};
class ConditionGuard
{
public:
ConditionGuard(void) noexcept
{
#ifdef IMPORTANT_CONDITION
CONDITION_ON();
#else
CONDITION_OFF();
#endif
}
#ifdef IMPORTANT_CONDITION
private: static void CONDITION_ON(void);
#else
private: static void CONDITION_OFF(void);
#endif
};
static ConditionGuard const condition_guard{};
// Foo.cpp - library implementation file
#include "Foo.hpp"
Foo::Foo(void) {}
#ifdef IMPORTANT_CONDITION
void ConditionGuard::CONDITION_ON(void) {}
#else
void ConditionGuard::CONDITION_OFF(void) {}
#endif
Now when user code includes library header Foo.hpp it will also trigger construction of condition_guard static variable which will call a library function depending on condition being protected. So if there is a translation unit including Foo.hpp where IMPORTANT_CONDITION is defined differently than in compiled library then there will be a linker error for missing CONDITION_ON or CONDITION_OFF. CONDITION_ON and CONDITION_OFF function names should contain error text.
One option is to just include the full code for A into project B. What are you trying to do by compiling A into a static library?
I think you're best option is to generate different .a files depending the target. i.e. libA_debug.a when TRACK_THREAD_OWNER_FOR_DEBUG is set, libA.a when it is not.
Then you could set the library to link B to based on whether you are compiling a debug or release version.
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.