dll hell again (please help)

Started by
14 comments, last by Kippesoep 18 years, 10 months ago
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 IOFILE *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]
Kippesoep
Advertisement
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?
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).
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.
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.
"E-mail is for geeks and pedophiles." -Cruel Intentions
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.
Kippesoep

This topic is closed to new replies.

Advertisement