Sign in to follow this  
JinJo

dll hell again (please help)

Recommended Posts

JinJo    100
I still cant get this working. This is to do with plugins in my app. I have a main exe project that uses SDL to load everything and it has a platform manager class that can load the specific renderer etc to be used. The renderer etc are stored as dll files. Now the main exe program creates singletons for the log file etc but I cannot use the log inside my renderer dll. I have the implementation of the singleton get functions overidden in the logs cpp file as well as setting the actual singleton to NULL. I used the OGRE export macro but with my engines name too and the log lass has the export prefix. In the properties of my main program i have declared the NON_CLIENT_BUILD part. Now my main app compiles but my renderer dll will not compile. I get errors like: OGLRenderer error LNK2001: unresolved external symbol "__declspec(dllimport) public: void __cdecl ENGINE::E_Log::Write(int,char const *,...)" (__imp_?Write@E_Log@ENGINE@@QAAXHPBDZZ) if I add the log file cpp file to the renderer dll project i get the error: h:\Programming\current Engine backup\stable\genesis 23-11-04-11-07\source\engine\E_Log.cpp(21): error C2491: 'ENGINE::E_Singleton<T>::m_Singleton' : definition of dllimport static data member not allowed with [ T=ENGINE::E_Log ] Don't know what's goin on there. Do any of you have any ideas as to how to solve this problem, it's driving me insane lol. [Edited by - JinJo on May 25, 2005 11:36:07 AM]

Share this post


Link to post
Share on other sites
JinJo    100
Just to clear up a bit, it isn't a question related to the OGRE engine, I just posted there as a few people were having the same problems, should I just post the detais here so no one needs to look at the link.

Share this post


Link to post
Share on other sites
Kippesoep    892
Fewer people are going to read your problem if you don't post it here. Copy/paste isn't really that difficult.

Does it work properly when you compile in Debug mode?

Share this post


Link to post
Share on other sites
JinJo    100
Well now the problem is outlined above, I have taken the non build define out of my main program and put it in my renderer dll.

I get no compiler errors now but when I run and the renderer tries to write using E_Log::GetSingleton().Write(...);
I get an assertion error message and the debugger in msvc.net opens up the ostream file.

Any ideas whats happening?

Share this post


Link to post
Share on other sites
JinJo    100
it says in log.cpp line 23, expression:m_singleton, which is where the log file implements the singletons get function and uses assert.

Removing this still causes it to break inside the ostream file, i dont know how to use the debugger of msvc well but i dont think thats the major problem.

The log is still workin in the main exe its only when called from the dll that it starts blowing up.

Also may i add that for some reson i have to include the log.cpp file in the dll project, which i think is stupid but ive had to do it with kernel and memory management stuff too as well as some utilities.

In this file there is the line, template<> E_Log* E_Singleton<E_Log>::m_Singleton = NULL;

I dont know if that is causing a problem.

Share this post


Link to post
Share on other sites
Kippesoep    892
Would you happen to be using MSVC6? AFAIK, That has a problem with using (its version of) the STL across process boundaries. A replacement, such as STLPort fixes that.

Share this post


Link to post
Share on other sites
JinJo    100
nah im using VC++.net 2003.

Well, I just added the lines in the dll renderer:
new E_Log();
E_Log::GetSingleton().Init();

now when I use the write function in the dll it does not cause an assert but it overwrites the original logfile, all this was expected.

So basically the dll is not getting the singleton pointer that the main exe is using. I knew that too, so how do I get the dll to use the same singleton pointer? Without passing in pointers to the dlls etc as this is dirty and OGRE doesn't need to do this.

[Edited by - JinJo on May 25, 2005 11:19:05 AM]

Share this post


Link to post
Share on other sites
JinJo    100
it's a static member.

the singleton .h file is as follows:
[Source]
template<typename T>
class E_Singleton
{
public:
static T* m_Singleton;
public:
E_Singleton(void)
{
assert(!m_Singleton);
int offset = (int)(T*)1 - (int)(E_Singleton<T>*)(T*)1;
m_Singleton = (T*)((int)this + offset);
}

~E_Singleton(void)
{
assert(m_Singleton);
m_Singleton = 0;
}
static T* GetSingletonPtr(void);

};
[/Source]


inside the log.cpp file there is the following code:
[Source]
template<> E_Log* E_Singleton<E_Log>::m_Singleton = NULL;
E_Log& E_Log::GetSingleton(void)
{
assert(m_Singleton);
return *m_Singleton;
}

E_Log* E_Log::GetSingletonPtr(void)
{
return m_Singleton;
}
[/Source]


Maybe it's something to do with that constructor code being in the header? but then surely i don't have to have all the classes that derive from a singleton implement the constructor too? It's bad enough having to do these acces methods for them all.

Kippesoep rating++ for the continuous help so far.

Share this post


Link to post
Share on other sites
Kippesoep    892
There has to be something odd about your code, because I took the parts that you posted and made a test project, which works just fine. Below is the test project (don't mind the ugliness -- it's quick and very dirty):

Client application (implements the log class):

defs.h

#define MY_DECLSPEC __declspec(dllexport)





Singleton.h

#include <cassert>

template<typename T>
class MY_DECLSPEC E_Singleton
{
public:
static T* m_Singleton;
public:
E_Singleton(void)
{
assert(!m_Singleton);
int offset = (int)(T*)1 - (int)(E_Singleton<T>*)(T*)1;
m_Singleton = (T*)((int)this + offset);
}

~E_Singleton(void)
{
assert(m_Singleton);
m_Singleton = 0;
}

static T& GetSingleton(void);
static T* GetSingletonPtr(void);
};





Log.h

#include "Singleton.h"

class MY_DECLSPEC E_Log: public E_Singleton <E_Log>
{
public:
static void Init ();
void Write (const char *s);
static E_Log& GetSingleton (void);
static E_Log* GetSingletonPtr (void);
};





Log.cpp

#include <cstdio>
#include <cstdlib>

#include "defs.h"
#include "Log.h"

template<> E_Log* E_Singleton<E_Log>::m_Singleton = NULL;

//Ugly global variable using C-style IO
FILE *f = NULL;

void E_Log::Init ()
{
//Create instance
assert (!m_Singleton);
m_Singleton = new E_Log;

//Open file - I don't even bother closing it
f = fopen ("LOG.TXT", "wt");
}

void E_Log::Write (const char *s)
{
fprintf (f, "%08X: %s\n", m_Singleton, s);
}

E_Log& E_Log::GetSingleton(void)
{
assert(m_Singleton);
return *m_Singleton;
}

E_Log* E_Log::GetSingletonPtr(void)
{
return m_Singleton;
}





main.cpp

#include "defs.h"
#include "Log.h"

#include <windows.h>

int main ()
{
//Begin
E_Log::Init ();
E_Log::GetSingleton ().Write ("Hello world!");

//Load the library - I don't even bother unloading it
HMODULE hlib = LoadLibrary ("../Library/Library.dll");

return 0;
}





DLL Library - uses the LIB generated by the client application (via pragma):

defs.h

#define MY_DECLSPEC __declspec(dllimport)





Library.cpp

#include <windows.h>
#include "defs.h"
#include "../Client/Log.h"

#pragma comment (lib, "../Client/Client.lib")

BOOL APIENTRY DllMain (HANDLE, DWORD reason, LPVOID)
{
if (reason == DLL_PROCESS_ATTACH)
E_Log::GetSingleton ().Write ("In DLL");
return TRUE;
}





Output to LOG.TXT (as expected) is:

00320FE0: Hello world!
00320FE0: In DLL


I hope this helps you find the bug in your code.

[Edited by - Kippesoep on May 25, 2005 2:44:49 PM]

Share this post


Link to post
Share on other sites
s_p_oneil    443
I believe I know how you can get it working. Take your singleton declarations out of the EXE and put them in a DLL. Whenever I have a project using DLL's, I create a CoreDll project. I put all common code and all process-wide singletons in that project. Then I make CoreDll a dependency of every other project, and problems like this pretty much disappear. Well, that's not entirely true. I tried to use the templatized singleton class from the Enginuity articles in a DLL, and that didn't work at all. Each module gets its own instance of static members declared in a templatized class. I got compile errors trying to declare the class as import/export.

EDIT: I just took a closer look at the code that's been posted. I have never been able to get __declspec() to work on templatized classes, and I've never seen the "template<> E_Log* E_Singleton<E_Log>::m_Singleton = NULL;" syntax. Does that solve the problem I mentioned above with each module getting its own instance of m_Singleton?

Share this post


Link to post
Share on other sites
JinJo    100
Thanks for that, i think u deserve another ratings++. It will take me about half an hour to go through it all in my code. One thing I wasn't doing was adding the exe's .lib file to the dll project (done it quickly and it still doesn't compile lol) I should be able to get it though since your code works.

s_p_oneil :
I will let you know when I go through this but I'm sure it does elimenate the need, that's why i'm trying this.

Thanks again. (will post back with results).

Share this post


Link to post
Share on other sites
JinJo    100
Well I have sorted that thanks to you, and now I have to do the same thing with my timer code as thats popping up with bugs. Cheers.

Share this post


Link to post
Share on other sites
ParadigmShift    190
Wait... How in the heck does this work?

In Windows, the DLL and executable don't share constants or statics. So unless you use a shared memory block, you should always get two copies of the "singleton". If you try to link the executable code into the library (wtf??) you may get some multiple definitions, but AFAIK there is no way to make the DLL loader map the corresponding data segment onto the executable's at runtime.

Can anyone explain the sleight-of-hand here? I'm missing something.

Share this post


Link to post
Share on other sites
Kippesoep    892
Quote:
Original post by ParadigmShift
Can anyone explain the sleight-of-hand here? I'm missing something.


Yup. No sleight-of-hand. What you're missing is that the singleton is created by the main executable (in the Init call). That variable exists only in the executable. (You can tell by the fact that the file Log.cpp is not used in the creation of the DLL) The executable exports the class interface only, which is imported by the DLL. This results in all calls on the object's methods being passed on to the code in the main executable.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this