Sign in to follow this  
arithma

DLL compatibility

Recommended Posts

arithma    226
I am developing this ai-experiment system. The main executable loads DLLs that are defined by users of the main exe. The main executable is built under Visual Studio 2005. What constraints would that put on the targeted developers? Would they be able to plug DLLs built in Visual C++ 6.0? Borland C++? What else should I do to maximize compatibility?

Share this post


Link to post
Share on other sites
Evil Steve    2017
Quote:
Original post by arithma
I am developing this ai-experiment system.
The main executable loads DLLs that are defined by users of the main exe.
The main executable is built under Visual Studio 2005.
What constraints would that put on the targeted developers? Would they be able to plug DLLs built in Visual C++ 6.0? Borland C++?
What else should I do to maximize compatibility?
If you're linking with a .lib file, then you might run into some problems. If you use runtime linking (LoadLibrary() and GetProcAddress()), you shouldn't have any problems with compatability.

Make sure you don't allocate memory in the EXE and free it in the DLL or vice versa (That includes passing STL objects over a DLL boundary except by const pointer / reference), or you'll run into all sorts of problems too.

Share this post


Link to post
Share on other sites
Kwizatz    1392
Do not export any C++ functions or objects from the DLL, or have any functions or objects imported from the DLL.

Wrap your exported function declarations with:


#ifdef __cplusplus
extern "C"
{
#endif
// declarations go here
#ifdef __cplusplus
extern "C"
}
#endif



Using C++ internally on either end is ok, but when you export it or import it you will have compiler conflicts due to name mangling, even if you think you can reconstruct the object, different compilers may have a different footprint on how they handle objects, so avoid it.

If you need to pass objects around you could just pass a pointer to them, and use that as a handle, of course then you would have to write functions on either side (depending on the origin of the object) to actually do something with it.

For example, lets say you have an object on your main program with a SetHealth function:

On the Main Program:

class entity
{
public:
void SetHealth(int newhealth){Health=newhealth;};
private:
int Health;
};
/* the following would be part of the Main Program API */
void* CreateEntity()
{
return new entity;
}
void SetEntityHealth(void* e,int health)
{
((entity*)e)->SetHealth(health);
}



On the DLL:

void* handle = CreateEntity();
SetEntityHealth(handle,100);



The same thing goes for objects created on the DLL, just reverse the example.

Share this post


Link to post
Share on other sites
Evil Steve    2017
As an alternative / addition to Kwizatz's suggestion, you can use abstract classes (Which is my favorite method).

Example from one of my projects:

You have a shared header like so:

#ifdef DRUINKPACKER_EXPORTS
# define DRUINKPACKER_LINKAGE __declspec(dllexport)
#else
# define DRUINKPACKER_LINKAGE
#endif

class IPackReader
{
IPackReader() {}
virtual ~IPackReader() {}

virtual bool OpenFile(const char* szFilename) = 0;
virtual bool OpenFile(const wchar_t* szFilename) = 0;
virtual void CloseFile() = 0;
};

extern "C" {
DRUINKPACKER_LINKAGE IPackReader* CreateReader();
DRUINKPACKER_LINKAGE void DestroyInstance(IPackReader* pPackFile);
}


Which the client program includes. You can use GetProcAddress() to get the address of the create and destroy functions, and you can call methods of the returned interface.

In the DLL you implement the class:

class PackReader : public IPackReader
{
public:
PackReader();
virtual ~PackReader();

// IPackReader:
virtual bool OpenFile(const char* szFilename);
virtual bool OpenFile(const wchar_t* szFilename);
virtual void CloseFile();
};



And in the DLL's .cpp file you implement the two exported functions, and any other functions in the derived class:

IPackReader* CreateReader()
{
return new PackReader();
}

void DestroyInstance(IPackReader* pPackFile)
{
delete pPackFile;
}

PackReader::PackReader()
{
// Implementation here
}

PackReader::~PackReader()
{
// Implementation here
}

bool PackReader::OpenFile(const char* szFilename)
{
// Implementation here
}

bool PackReader::OpenFile(const wchar_t* szFilename)
{
// Implementation here
}

void PackReader::CloseFile()
{
// Implementation here
}




Now everything is nice and OOPy in both the DLL and the client side EXE.

Share this post


Link to post
Share on other sites
arithma    226
I can't use static binding so am safe.
Well I love the OOP idea, and it's totally compatibility-wise safe, right? Since the OOness remains seperated between the DLLs and the EXE.
As for the extern "C", I only enclose the function prototypes with that?

Share this post


Link to post
Share on other sites
Kwizatz    1392
Quote:
Original post by arithma
As for the extern "C", I only enclose the function prototypes with that?


Yes, just the prototypes should be enough, obviously they should reside on a header which is included by the implementation of the functions, the compiler/linker is smart enough to know the that the not marked implementations belong to the extern "C" prototypes.

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