Dynamic Link Libraries

Published June 12, 2005
Advertisement
Handling Resources
DLLs have their strong points, but one of their weak points deals with memory. In general: If you link to a DLL statically, it will be placed in your process space, and thus share your heap. When you allocate memory, you can free it from the application or the DLL, since both are using the same heap. However, if you are using dynamic linkage, then you aren't so lucky. The DLL will have it's own heap, and hence memory allocated by the DLL must be released by the DLL, not by the application. Trying to release the memory that the DLL allocated in the application can result in a variety of different bugs, most of the time, however, it will just crash with an access violation.

In general, you should stick to the following simple rule: 'He who allocates it, deallocates it.' Basically, if the DLL allocates a chunk of memory, then it should be the one to deallocate it. Of course, the case here is not so clear cut as one would hope. Passing strings, for instance, can result in under the cover allocations and copy operations, along with their associated deallocations. The easiest way to avoid problems is to either pass raw data, such as character pointer, or to pass constant references. This will force the user to make a copy to make changes, thus preventing allocation errors.

Other tricks involve providing custom allocators to your standard library which all allocate from the same heap. This solution, however, is more complex in general than just obeying the simple rule I stated above, but can have certain advantages.

Our first DLL
First of all, this assumes Visual Studio, and that you've created an empty DLL project and added a source file and a header.
//simple.h#ifndef SIMPLE_AA9B8090_F6C7_4a9c_8858_22D4C92B1EC7#define SIMPLE_AA9B8090_F6C7_4a9c_8858_22D4C92B1EC7#ifdef _MSC_VER#ifdef SIMPLEDLL_EXPORTS#define DLLIMP __declspec(dllexport)#else  //SIMPLEDLL_EXPORTS#define DLLIMP __declspec(dllimport)#endif //SIMPLEDLL_EXPORTS#else  //_MSC_VER#define DLLIMP#endif //_MSC_VER#include struct IStrange {	virtual std::string const& GetName() const = 0;	virtual void PrintString(std::string const& str) = 0;	virtual void Release() = 0;protected:	virtual ~IStrange() = 0 {};};extern "C" DLLIMP IStrange* GetStrange();typedef IStrange* (*GetStrangeSignature)();#endif //SIMPLE_AA9B8090_F6C7_4a9c_8858_22D4C92B1EC7

First off, the macros should be pretty self explanatory, SIMPLEDLL_EXPORTS is defined for me by Visual Studio (it's in the build options under C++) and is the name of the project with EXPORTS appended to it. If that is defined, then DLLIMP will be replaced with __declspec(dllexport), which marks a function as being exported. Otherwise it is replaced by __declspec(dllimport), which marks a function as being imported.

The structure is used to define an interface with three pure virtual functions, note the constant referenced strings. You should also note that the destructor is protected. This prevents the user from calling delete on an IStrange pointer. We also export a single function, GetStrange, which returns a pointer to an IStrange interface. We also provide a typedef of the function signature of the GetStrange function, which I'll demonstrate the usage of later.

Next we wish to create a class that implements the IStrange interface.
#ifndef STRANGEIMPL_F86F9C31_5E3B_4f88_82E4_B8EA1F1BAB26#define STRANGEIMPL_F86F9C31_5E3B_4f88_82E4_B8EA1F1BAB26#include "Simple.h"class StrangeImpl : public IStrange {public:	StrangeImpl();	~StrangeImpl();	std::string const& GetName() const;	void PrintString(std::string const& str);	void Release();private:	std::string name;};#endif //STRANGEIMPL_F86F9C31_5E3B_4f88_82E4_B8EA1F1BAB26

As we can see above, the class is just a simple implementation of the base class, with the addition of a constructor and a single private variable. The definition of the methods:
#include "StrangeImpl.h"#include StrangeImpl::StrangeImpl() : name("StrangeImpl") {}StrangeImpl::~StrangeImpl() {}std::string const& StrangeImpl::GetName() const {	return name;}void StrangeImpl::PrintString(std::string const& str) {	std::cout<}void StrangeImpl::Release() {	delete this;}IStrange* GetStrange() {	return new StrangeImpl();}

Note the GetStrange method is found here, and that it returns an instance of our derived class. Another thing that you should note is that the GetName method returns a constant reference to the name string, which is initialized in the constructor to the name of our class...for no particular reason.

If you compile and link this, you will end up with two output files, a dll file, and a lib file. The lib is used when you wish to statically link to a DLL. For both dynamic and static linking, you are required to have the DLL file available.
0 likes 5 comments

Comments

Mushu
I am much pleased! [grin]

After reading the notes on unshared resources, however, I see there isn't much I should be using them for at this point. Which is probably a good thing - I'd probably end up hurting myself somehow anyway.



EDIT: was browsing my FTP and found that ^. Swapped with previous image.
June 12, 2005 08:55 PM
Washu
heh, i have that image...I've also got an S&M one...mmmmm straps
June 12, 2005 09:02 PM
Saruman
What is the performance difference of using the library dynamically compared to linking statically if any?

Right now I have both renderers just as projects in the solution and I say which I would like to compile against. It is not an optimal solution but works for the time being while building it up. Although if there is only a negligable performance hit I think I am going to switch my renderers to .DLLs which would be much better.

Instead of statically linking my engine to the application would it be fine to have the engine as a DLL, renderers as DLLs, etc? Or will I start incurring performance loss at some point?
June 12, 2005 09:37 PM
Washu
Well, there's pretty much just an extra pointer dereference, if that.
June 12, 2005 09:44 PM
Galapaegos
Another idea I've seen mentioned in regards to sharing memory across a process and a dll is to make the objects "com-like". Basically, something like this:

//header
class Object
{
protected:
   Object () {}
   ~Object () {}

   Object *create ();
   void release ();
}

//cpp file
Object *Object::create () { return new Object; }
void Object::release () { delete this; }


Do you think this is a viable solution to the memory between a process and its libraries? Instead of delete/free-ing them, you would just call the release function and it will release the object from the heap it was allocated on. I've been looking for some options other than having static linkage.

-brad
July 29, 2005 06:49 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement