• Advertisement
Sign in to follow this  

dll memory allocation

This topic is 4681 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Here's the problem: I've got a DLL that contains a class with functions for loading different types of 3D data files like .obj. I wrote these functions in a DLL so that I could change my readers and not have to recompile every executable have reads 3D data. The problem is that I don't know how to allocate memory within a DLL and then pass it back to my executable. I found that memory allocated within the DLL cannot be deleted from the executable so I had to cheat and pass free(), malloc(), and realloc() to the class constructor and use pointers to those functions to deal with memory in the DLL. Is there a way to allocate memory within a DLL without requiring my executable to pass pointers to its memory allocation routines?

Share this post


Link to post
Share on other sites
Advertisement
You can pass memory allocated from a dll to an exe, the tricky part is deleting it. What I tend to do is use either a factory to create objects. Or derive your classes from a base that overloads new and delete for the class. THat way all news and deletes for those classes regardless of if its called in the exe or dll will go to the proper heap.

CHeers
CHris

Share this post


Link to post
Share on other sites
I've alwaysed used a create/release pattern like this:



//-------------- Object.h --------------

class Object
{
public:
static Object* create(void);
void release(void);
private:
Object(void);
~Object(void);
};

//-------------- Object.cpp --------------

#include "Object.h"

Object*
Object::create(void) // It's VERY important that this method is implemented in the .cpp file
{
return new Object;
}

void
Object::release(void) // It's VERY important that this method is implemented in the .cpp file
{
delete this;
}

Object::Object(void)
{
}

Object::~Object(void)
{
}



This way there's only possible to allocate/free an Object's memory inside Object.cpp.
Thanks to only having protected/private constructors and destructor, there's no way a user can get an Object without using the create method (will get compiler errors).

If Object.cpp was compiled into an .DLL all allocation/freeing will be done using the DLL's heap. You can also get problems similar to the .dll when overriding allocators.
Consider:



// ------- Foo.cpp

#include "Bar.h"

...
myObject = new Object; // Assuming it's an ordinary object not using a create/release scheme
doStuffWithObjectAndDelete(myObject);
...

// ------- Bar.h

#include "Object.h"

void doStuffWithObjectAndDelete(Object* object);

// ------- Bar.cpp

#include "MySuperiorAllocatorWithLoggingFunctionality.h" // <= This overloads operator new/delete.
#include "Bar.h"

void
doStuffWithObjectAndDelete(Object* object)
{
// TODO: do stuff with object
delete object; // <= Uses the overloaded delete while it should use the "normal"
}



Using a create/release pattern is very safe, I've been using it for quite some time now and I'm very happy with it.


Share this post


Link to post
Share on other sites
Eq's solution is nice if you don't need to create stack based objects. If you want stack based objects you'll either have to wrap them or use another solution.

Cheers
Chri

Share this post


Link to post
Share on other sites
EQ, I see what you're saying and I like the idea of restricting access. The problem for me is that the data I pass back from the DLL needs to be available to edit so I need to realloc and free from within the .exe on demand. Unless of course I misread what you said, in which case i'll go jump off a bridge... my computer will probably go first to cushion the fall.

Chollida, that's my problem, deleting crashes the exe. When you say create a base class do you mean using my DLL class as a base for my object classes so when they delete data it calls the .dll removal instead of the exe?

Share this post


Link to post
Share on other sites
I mean derive all your classes in teh dll that need to allocate memory from a base class. All this base class would do is overload new and delete to call the global versions of them. That way you are sure that when you use new and delete with these objects your calls go to your own code in the dll and that ensures that they always use the correct pool.

Cheers
Chris

Share this post


Link to post
Share on other sites
Quote:
available to edit so I need to realloc and free

Don't quite follow what you're trying to do.
It's perfectly valid for your exe to manipulate an object allocated by an dll, or?
An example on how to use:


// ====== Exe.cpp => Exe.exe

#include "Dll.h"

int
main(void)
{
Object* object = Object::create();
if (!object)
return 0;
object->doSuff(); // Use object as any normally created object.
object->release(); // Release when done.
}

// ====== Dll.h

#ifdef _USER_DLL
#define DLLDECL __declspec(dll_export)
#else//_USER_DLL
#define DLLDECL __declspec(dll_import)
#endif//_USER_DLL

DLLDECL class Object
{
public:
static Object* create(void);
void doStuff(void);
void release(void);
private:
Object(void);
~Object(void);
};


// ===== Dll.cpp => Dll.dll

#include "Dll.h"

Object*
Object::create(void)
{
return new Object;
}

void
Object::release(void)
{
delete this;
}

Object::Object(void)
{
}

Object::~Object(void)
{
}

void
Object::doStuff(void)
{
OutputDebugString("Stuff!\n");
}



(Not complete source).

Share this post


Link to post
Share on other sites
If you want memory ownership to flow painlessly between the host exe and DLL(s), you could use a dll runtime library, which will result in all modules using the same heap.

This can prove useful for things like tracking memory leaks and such as well (if you're reliying on the runtime's built in leak tracking).

Share this post


Link to post
Share on other sites
Example



#include "Dll.h"
//void ReadObj(char *file,3D_Point **tmp, int *numPts);

struct 3D_Point {
float x,y,z;
};
3D_Point *points; //Pointer to our 3D vertex data
int numPts; //Number of vertices

void RemoveFeet();

void main(){
//Init Loader DLL
Object load;
load.ReadObj("Test.obj",&points,&numPts); //Read our Test.obj file.
RemoveFeet();
}

void RemoveFeet(){

/*Assume some data initialization here. (Removed for brevity)
Lets say we know every point 1 inch from the group is to be removed.*/


/*Also assume that prior to this point I sort all the vertices from
largest to smallest so that once I find the first foot point I can
remove the rest without searching.*/


int count = 0;

while(count < numPts) {
if(points.y < 1/*inch*/)
break; //From while loop
count++;
}
/*We know every point from 'count' to the end of our array is part of the
foot, remove it.*/

points = (3D_Point*)realloc(points, sizeof(3D_Point)*(numPts - count));
numPts -= count;
}





I know it's a bad example but it's the best I could come up with on short notice and without my actual source code handy. See I don't care about the Object class, it can do whatever it wants. What I want to free and mess around with is the points array that was allocated within the Object dll. Also note that this might actually work, I'm having problems with realloc but I don't know if they are because of the DLL or some other problem. I know for sure free() doesn't work.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Another "nice" feature of the create/release pattern is that it's dead simple to add refcounting. Since you know for sure that the users of the class can't call the destructor, you can just decrease the refcount in the release method, and destroy the object once it reaches zero.
Another "benefit" is that if your object is quite complex, your construcor might fail (i.e loading stuff etc). The normal way of handlig that is to set a flag and then for every method test this flag. I.e:


struct Object
{
Object(const std::string& filename)
{
m_valid = FileSystem::loadData(m_data, filename);
if (m_valid){ // Validate data
m_valid &= (m_data[0] == 'M');
m_valid &= (m_data[1] == 'S');
m_valid &= (m_data[2] == 'H');
m_valid &= (m_data[3] == '0');
m_valid &= (m_data.size() > 8);
}
}
int getMaterialCount(void) const
{
if (!isObjectValid()) // The error in the constructor is handled later
return 0;
return int(m_data[4]); // Material count is in the forth byte
}
private:
inline bool isObjectValid(void) const
{
return m_valid;
}
bool m_valid;
std::vector<char> m_data;
}

void
main(void)
{
Object o("Test.msh");
int c0 = o.getMaterialCount(); // <= Object validation..
int c1 = o.getMaterialCount(); // <= ..on every usage.
}



One could give the user the "rule" that after each construction the user must validate the object, like:


struct Object
{
Object(const std::string& filename)
{
m_valid = FileSystem::loadData(m_data, filename);
if (m_valid){ // Validate data
m_valid &= (m_data[0] == 'M');
m_valid &= (m_data[1] == 'S');
m_valid &= (m_data[2] == 'H');
m_valid &= (m_data[3] == '0');
m_valid &= (m_data.size() > 8);
}
}
inline bool isObjectValid(void) const // Must be called after object creation! If false is returned the object is invalid and MUSTN'T be used!
{
return m_valid;
}
int getMaterialCount(void) const
{
return int(m_data[4]); // Material count is in the forth byte
}
private:
bool m_valid;
std::vector<char> m_data;
}

void
main(void)
{
Object* o = new Object("Test.msh");
if (o){ // A lot of people (including photoshop progammers etc) doesn't handle out of memory cases.
if (o->isObjectValid()){
int c0 = o->getMaterialCount();
}
delete o;
}
}



But what if a user "misses" or forgets to call the validation method, like:


void
main(void)
{
Object* o = new Object("Test.msh");
if (o){
int c0 = o->getMaterialCount(); // Program will cause an Access violation or return invalid data that probably will cause an access violation later.later.
delete o;
}
}



If your creating objects using a create method you can do the following:

struct Object
{
static Object* create(const std::string& filename) // Should really be in a .cpp
{
Object* o = new Object;
if (!o)
return null;
if (!o->onCreate(filename)){
o->release(); // Delete object if an error occurs..
return null; // ..and return null
}
return o;
}
int getMaterialCount(void) const
{
return int(m_data[4]); // Material count is in the forth byte
}
void release(void) // Should really be in a .cpp
{
delete this;
}
private:
bool onCreate(const std::string& filename)
{
if (!FileSystem::loadData(m_data, filename))
return false;
if (m_data[0] != 'M')
return false;
if (m_data[1] != 'S')
return false;
if (m_data[2] != 'H')
return false;
if (m_data[3] != '0')
return false;
return m_data.size() > 8;
}
// bool m_valid; // No extra data needed.
std::vector<char> m_data;
}

void
main(void)
{
Object* o = new Object("Test.msh"); // If we're out of memory or the initiation fails null is returned
if (o){
int c0 = o->getMaterialCount(); // No validation needed.
o->release();
}
}



With this method the user CAN'T get a pointer to an invalid object.
People could ofcourse skip to test for a returned null pointer, but atleast it's easier to detect than doing work on random data.

I'm using the create/release pattern for all complex classes, not only for DLL's. I find it harder to "create" bugs this way. My 2c.

Share this post


Link to post
Share on other sites
Another "nice" feature of the create/release pattern is that it's dead simple to add refcounting. Since you know for sure that the users of the class can't call the destructor, you can just decrease the refcount in the release method, and destroy the object once it reaches zero.
Another "benefit" is that if your object is quite complex, your construcor might fail (i.e loading stuff etc). The normal way of handlig that is to set a flag and then for every method test this flag. I.e:


struct Object
{
Object(const std::string& filename)
{
m_valid = FileSystem::loadData(m_data, filename);
if (m_valid){ // Validate data
m_valid &= (m_data[0] == 'M');
m_valid &= (m_data[1] == 'S');
m_valid &= (m_data[2] == 'H');
m_valid &= (m_data[3] == '0');
m_valid &= (m_data.size() > 8);
}
}
int getMaterialCount(void) const
{
if (!isObjectValid()) // The error in the constructor is handled later
return 0;
return int(m_data[4]); // Material count is in the forth byte
}
private:
inline bool isObjectValid(void) const
{
return m_valid;
}
bool m_valid;
std::vector<char> m_data;
}

void
main(void)
{
Object o("Test.msh");
int c0 = o.getMaterialCount(); // <= Object validation..
int c1 = o.getMaterialCount(); // <= ..on every usage.
}




One could give the user the "rule" that after each construction the user must validate the object, like:


struct Object
{
Object(const std::string& filename)
{
m_valid = FileSystem::loadData(m_data, filename);
if (m_valid){ // Validate data
m_valid &= (m_data[0] == 'M');
m_valid &= (m_data[1] == 'S');
m_valid &= (m_data[2] == 'H');
m_valid &= (m_data[3] == '0');
m_valid &= (m_data.size() > 8);
}
}
inline bool isObjectValid(void) const // Must be called after object creation! If false is returned the object is invalid and MUSTN'T be used!
{
return m_valid;
}
int getMaterialCount(void) const
{
return int(m_data[4]); // Material count is in the forth byte
}
private:
bool m_valid;
std::vector<char> m_data;
}

void
main(void)
{
Object* o = new Object("Test.msh");
if (o){ // A lot of people (including photoshop progammers etc) doesn't handle out of memory cases.
if (o->isObjectValid()){
int c0 = o->getMaterialCount();
}
delete o;
}
}




But what if a user "misses" or forgets to call the validation method, like:


void
main(void)
{
Object* o = new Object("Test.msh");
if (o){
int c0 = o->getMaterialCount(); // Program will cause an Access violation or return invalid data that probably will cause an access violation later.later.
delete o;
}
}




If your creating objects using a create method you can do the following:

struct Object
{
static Object* create(const std::string& filename) // Should really be in a .cpp
{
Object* o = new Object;
if (!o)
return null;
if (!o->onCreate(filename)){
o->release(); // Delete object if an error occurs..
return null; // ..and return null
}
return o;
}
int getMaterialCount(void) const
{
return int(m_data[4]); // Material count is in the forth byte
}
void release(void) // Should really be in a .cpp
{
delete this;
}
private:
bool onCreate(const std::string& filename)
{
if (!FileSystem::loadData(m_data, filename))
return false;
if (m_data[0] != 'M')
return false;
if (m_data[1] != 'S')
return false;
if (m_data[2] != 'H')
return false;
if (m_data[3] != '0')
return false;
return m_data.size() > 8;
}
// bool m_valid; // No extra data needed.
std::vector<char> m_data;
}

void
main(void)
{
Object* o = new Object("Test.msh"); // If we're out of memory or the initiation fails null is returned
if (o){
int c0 = o->getMaterialCount(); // No validation needed.
o->release();
}
}




With this method the user CAN'T get a pointer to an invalid object.
People could ofcourse skip to test for a returned null pointer, but atleast it's easier to detect than doing work on random data.

I'm using the create/release pattern for all complex classes, not only for DLL's. I find it harder to "create" bugs this way. My 2c.

Share this post


Link to post
Share on other sites
Hmm the AP post seems to cut away some source, strange...

Share this post


Link to post
Share on other sites
eq

You test for memory failures by checking if the pointer is NULL, New throws an exception in most cases. Its funny that you make fun of photoshop programmers by not checking for out of memory error's but then you get it wrong:). You have to catch the exception:) not check for a null pointer as your code will never be executed:)

Please don't mock others if you don't understand the rules yourself.

Cheers
Chris

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement