[C++] Dll Issue

Started by
9 comments, last by force_of_will 15 years, 11 months ago
In the process of exporting some classes to a dll. I created an header for the dll containing this code and some more not relevant for the issue at hand. What made me loss like 10 minutes watching to to the code blindly what that i had the method Play, switched with Mute (in terms of order of appearence in the header i had created ) And that simple change made the code that uses the dll, call Mute instead of Play. I want to know what causes this. The symbols exported to the dll are exported in the order they appear. And that caused Mute symbol to be confused with Play ?

class DLL_API ISoundSystem		
{
public:
	ISoundSystem(void){	}

	virtual ISoundHandler * CreateSound(const char *name_or_data, SOUND_MODE mode) = 0;
	virtual bool PlaySound(ISoundHandler *sound, ISound ** channel, bool loop) = 0;	
	virtual bool UnloadSound(ISoundHandler **sound) = 0;

	virtual bool Pause(ISound *channel);
	virtual bool Mute(ISound *sound);
	virtual bool Play(ISound *channel);
	virtual bool SetVolume(ISound *sound, float vol);	// 0.0 silent - 1.0 full volume

	virtual bool Stop(ISound *sound);


	virtual void Update() = 0;

	virtual bool SetMasterVolume( float volume ) = 0; // set the master volume for all sounds (0.0 silent - 1.0 full volume )
	virtual bool IsPlaying() = 0;	// is any sound playing ?
	virtual ~ISoundSystem(void)	{ }
};




[Edited by - force_of_will on May 15, 2008 2:11:40 PM]
Advertisement
I have never messed much with DLLs but it sounds like something is out of sync - have you tried doing a full rebuild? If you swap the two functions like that of identical prototypes, it would not surprise me if now the functions had swapped memory addresses.
Yes i tried a full rebuild, the solution has 2 projects the 1º generates the dll and the second is a test (audio) which is dependent on the first. So i dont get why this happens, i forget to tell that i use a 3º header in which i do the swap.
That 3º header is the one i use on the second project.
One tried and tested method in exporting classes via DLL's is to use a "Factory Function". Basically an exported function, defined in the DLL itself, which returns a pointer (or more specifically an interface pointer) to a particular class that you want in the DLL itself.

How one goes about implementing this is quite varied and COM uses a particular model which, theoretically speaking at least, allows a client to access your DLL classes using COM API functions like CoCreateClass and CoCreateInstance, even if the client is NOT on your host machine (i.e. a remote client).

I'm not sure what approach you're taking, but if you don't have a Factory Function defined in your DLLs, which would allow clients to easily access your DLL classes, then I would strongly suggest that you try this approach.

You can easily implement such a function, without resorting to COM (although I've designed all my DLLs to be COM compliant). Such an approach helps with encapsulation, since all you need in your client code are interfaces to the classes. I.e. abstract data types that simply exposes the methods that you want your clients to use.

The actual implementation code in your DLL will of course have to expose the full class itself, including its data members, but the DLL is self contained anyway.

I'm not sure if this is exactly what you're looking for, but if this approach sounds good to you, then bump this thread again with a request and I'd be happy to show you some code that I use and some references on the Net.
I have simulated the same situation here with these files

Test.h
#pragma once#include "Base.h"class __declspec(dllexport) Base{public:	virtual bool Play();	virtual bool Nothing();	virtual bool Stop();	Base(void);	virtual ~Base(void);};class Test : public Base{public:	Test(void);		virtual bool Play();	virtual bool Nothing();	virtual bool Stop();		virtual ~Test(void);};__declspec(dllexport) Base *   newTest();

Test.cpp
#include "Test.h"#include<iostream>Base::Base(void){}Base::~Base(void){}bool Base::Play(){	return true;	}bool Base::Stop(){	return false;}bool Base::Nothing(){	return true;}Test::Test(void){}Test::~Test(void){}bool Test::Nothing(){	return true;}bool Test::Play(){	std::cout << "Play" << std::endl;		return true;}bool Test::Stop(){	std::cout << "Stop" << std::endl;		return false;}__declspec(dllexport) Base *  newTest(){	return new Test;}


DLL.h (which is the header i create to supply to the users of the dll
the problem happens when i change the order of declarations here)

#pragma onceclass __declspec(dllimport) Base{public:	Base(void);		virtual bool Stop();  // <-- swaped play with stop	virtual bool Nothing();	virtual bool Play();	virtual ~Base(void);  // <-- swaped stop with play			};__declspec(dllimport) Base  * newTest();



main.cpp the problem is that
a->Play() in fact calls Test::Stop() instead of Test::Play()
#include "../dll/DLL.h"int main(){	Base *a = newTest();	a->Play();	return true;}



I just want to know why this behavior happens.
(Something to do with virtual table ?, or something im doing wrong, or its just that for this to work the declarations on the header i supply to the users of my library myst follow the exact order in declaration of members of class's ? (strange if so and good way to make errors hard to find...)
[/source]
Don't member functions have to be dllexport'ed? (I don't know, I've only made DLL's with C interfaces)
Quote:Original post by MJP
Don't member functions have to be dllexport'ed? (I don't know, I've only made DLL's with C interfaces)


No, you can definitely export the whole class with a single 'dllexport' after the 'class' and before your class name.

Quote:Original post by force_of_will
(Something to do with virtual table ?, or something im doing wrong, or its just that for this to work the declarations on the header i supply to the users of my library myst follow the exact order in declaration of members of class's ? (strange if so and good way to make errors hard to find...)

So you're not compiling with the same header as your clients are including? That seems bad... why would you not use the same one? It seems more error prone to use two different headers. I can't say for sure whether using two separate copies with the member functions in different orders is actually causing your problem, but it certainly sounds suspect.
Quote:Original post by emeyex
No, you can definitely export the whole class with a single 'dllexport' after the 'class' and before your class name.


Well that's good to know. [smile]

I'm a bit confused...

You create a DLL file using one header file, then change the header file and use the modified header to link to the DLL?

If that's the case, then of course it's not going to work. The DLL is exporting classes in the order defined in the header file when it was built, if that order changes, then the functions called will change (And if you're really lucky it won't crash [smile]).

Returning an instance of a pure virtual class is exactly like returning a struct of function pointers; The order of the data is defined by whatever is in the header. I.e.:
typedef void (*FunctionPointer)();#define FUNC_PLAY       0#define FUNC_PAUSE      1#define FUNC_STOP       2#define FUNC_HAMMERTIME 3struct Base{   FunctionPointer vtable[4];};
That declares a struct of function pointers, and defines indices into the array of function pointers, which is more or less what a vtable is. If you then change the header in the EXE only, so you swap FUNC_PLAY and FUNC_STOP, then the functions returned by the DLL are still in the previous order (Play, Pause, Stop, Hammertime), but you're just referencing the wrong one in the exe.
Original post by Evil Steve
I'm a bit confused...

You create a DLL file using one header file, then change the header file and use the modified header to link to the DLL?

If that's the case, then of course it's not going to work...
[\quote]

I always assumed that they were exported by name with dllexport and not by ordinal for instance if i change the project type from dll to a simple console app, and try to simulate the same behaviour , no error happens.

Besides the point of header files are just to tell the linker that, the class exists and the methods inside it also exist, in another translation unit.

Taken from Msdn
The exports table contains the name of every function that the DLL exports to other executables. These functions are the entry points into the DLL; only the functions in the exports table can be accessed by other executables.
[\quote]

This topic is closed to new replies.

Advertisement