Solved

Derived Classes and DLLs

Posted on 2000-04-16
9
363 Views
Last Modified: 2013-12-14
Here's my problem.  So far, I have been programming some DLLs with __declspec(import and export)  I have just classes  (and callback functions, which are not my problem so n/m)  I am making a rendering class, with a base class, and modules for differnet renderers, DirectX, OpenGL, SoftWare, etc.  I have the basic Render class has the basic functions with no definitions, and the derived classes are the actual rendering devices that have the functions (from the base class)  are defined.  My problem is this, I want to have the base class compiled with the __declspec() but the derived classes to be loaded dynamically so that all rendering devices are not all loaded at once, and this will make room for people to compile their own rendering modules themselves.  I have read how to load simple functions from a DLL with LoadLibrary, GetProcAddress (or whatever)  and all those functions, but I want to know how to load an entire class, without having to typedef each seperate function then prototypeing from the typedef then loading from the DLL to the prototype, this just seems like too much work and I hope there's a quicker and more simplisitic way, without having to have multiple headers for the classes for build and runtime.  Can anyone help?  (Or understand what I mean)
0
Comment
Question by:XycsoscyX
  • 5
  • 2
  • 2
9 Comments
 
LVL 22

Accepted Solution

by:
nietod earned 75 total points
ID: 2721187
This can be done by using virtual functions (it would be nearly impossible without them).  I think I have a "prewwritten" answer on this.  I'll see if I can find it.
0
 
LVL 22

Expert Comment

by:nietod
ID: 2721189
Example.
************ Base.h*******
// Note no export directive.  Not of any value for you
// since you are linking at run-time.
class Paths {
 public :
         Paths() ;
        // note all functions are virtual, because linking
        // is to be done through the VTBL  No need for
        // GetProcAddress().
         virtual ~Paths() = 0;;
         virtual int DoSometingWith (int x, int y)  = 0;
         vitual char* SomethingElse (int v, char *w) = 0 ;
};

// Type defintition for the "factory" function.
typedef Path * (*MakePtrTyp)();

***********Dll.cpp************
#inlude "Base.h"

// Derived concrete class.  Functions can be added here that
// are not virtual, so long as they are not to be used by the EXE.
// Any functions the EXE uses must be virtual.
class DerivedPaths : public Paths
{
public:
         DerivedPaths() ;
         virtual ~Paths()  {};
         virtual int DoSometingWith (int x, int y)  { return x+y; };
         vitual char* SomethingElse (int v, char *w)  { return w+v; } ;
}

// The factory function.  Uses extern "C" so that it is easier for
// the EXE to get using GetProcAddress();
extern "C" Paths *MakePaths()
{
    return new DerivedPaths;
};

***************EXE.cpp*****************
WinMain()
{
   HINSTANCE PathLib = Loadlibrary("some path");
   MakePtrTyp MakeProc = (MakePtrTyp) GetProcAddress(PathLib,"MakePaths");

   Paths *PathsPtr = (*makeProc)(); // Make a paths object.

   PathsPtr->DoSomethingWith(1,2);
   delete PathsPtr;
}

A couple more words of explanation.  

The only time you need to use GetProcAddress() with this scheme is to find the factory function in the DLL that creates the objects.  All the other functions are handled through virtual functions calls.  That is obviously much more convenient than lots of GetProcAddress() calls!

The DLL allocates objects using new and the EXE eventually deletes it with delete.  For this to work the two must share a common heap, this means you must use the DLL version of the run-time library.  (a good idea in any case.)  Another alternative is for the DLL to have a function that does the delete, but I would do the other.

The EXE can load different DLLs that define different derived classes.  So if the derived class has different versions or changes with time, the EXE does not need to be recompiled.

This approach is in many ways the C++ version of COM.
0
 
LVL 22

Expert Comment

by:nietod
ID: 2721199
That is an example, but not an explanation.  What you need to do is to make all the public functions of the class virtual.  And you do not need to export them from the DLL (but you can, it just doesn't matter).  This is so that the virtual table will be used to find the right procedure to call at run time.  This is is important because you (realistically) cannot use the return value from GetProcAddress() to call a non-static member function.  So we get around this by using virtual functions.

Now you will need to use GetProcAddress() still, but only for one thing, which is nice because it is inconvenient to use!  when you need to create a object you will use GetProcAddress() to call a factory function in the DLL.  To be specific, each DLL will have an exported function with a specific name (or ordinal) and this function will return a pointer to a dynamically allocated object of whatever class this DLL is supposed to create.  Now the object returned by the DLL is of a class derived from the class the EXE "knows" about.  The EXE doesn't "know" about the derived class.  But that is fine.  The factory function creates the class and the virtual functions manipulat the class, so the EXE only needs to "know" about the base class.

Let me know if you have any questions.
0
Comprehensive Backup Solutions for Microsoft

Acronis protects the complete Microsoft technology stack: Windows Server, Windows PC, laptop and Surface data; Microsoft business applications; Microsoft Hyper-V; Azure VMs; Microsoft Windows Server 2016; Microsoft Exchange 2016 and SQL Server 2016.

 

Author Comment

by:XycsoscyX
ID: 2721562
I accepted the answer because it works now, with one workaround.  When I compiled at first, I got an error  (when I compiled the DLL for the derived class)  Since I have to use the base class as : public RenderSystem (which is what I am using)  But I get an error when I compile saying the Initializer and DeInitializer from the base class are not defined.  My temporary workaround was to define the Init and DeInit within the Derived class modules seperately, but I want to have the functions for the Init and DeInit of the base class with the exe itself, not the seperated modules.  How can I fix this?
0
 
LVL 22

Expert Comment

by:nietod
ID: 2722406
I'm not really following you.  Are you tallking about constructors and destructors, or other functions?

hmmmm.

Did you get this error when compiling the EXE or the DLL?  You might get this when compiling the DLL....   If so, I think you're going to have to place these functions in the base class's include file, so they are placed in each module.  

another option, might be to export the the functions from the class in the EXE and then link the DLL with the EXE's .lib file.   That sort of technique is actually posible.  (havig a DLL use exports from the EXE that loads it.)  but I've never done it and there may bother steps invloved and adding the fact that these are member functions could compilcate it.
0
 

Author Comment

by:XycsoscyX
ID: 2724174
Okay, thanks, I thik what I wil to is make my base renderer a dll  (well, actually base render, input, sound, etc)  and export the classes to it, so I have it and the dll, then link my derived classes to the lib from the base dll, and link the exe to this base dll also.....thank for all your help.
0
 

Expert Comment

by:gpbaldazzi
ID: 4305554
I used the code Nietod code and I find it great!
But I have a trouble: my "MakePaths" function is exactally the same, I can use pointers returned by this function to call virtual function and everithing is all right.
But my program crashes vhen I call the
"delete" on this pointers!

Any suggestion?
Thanks
GP
0
 
LVL 22

Expert Comment

by:nietod
ID: 4305797
Are you using the DLL version of the run-time library (RTL)?  You need to.  The static RTL will not work for this.

From another question:

**********************************

The problem is that if the EXE and DLL use the staticly linked version of the run-time library (RTL), they each have their own seperate copies of the RTL.  These copies each have thier own seperate heaps for allocating memory with the new operator.   The problem is that each one does not "know" about the other.  So for example, if the DLL allocates memory, the memory comes from the heap in the DLL's copy of the RTL.  If a pointer to that memory is passed back to the EXE (it may be passed in a subtle way, like inside a class) and if that EXE later makes changes that require that the memory specified by the pointer be deleted, then the EXE will try to delete the memory, but will not be able to find the memory inside its heap.  This is is because the memory did not come from the heap.  Hence the problem.

The solution is to have the EXE and all the C++ DLLS that it uses link with the DLL version of the RTL.  Then the EXE and all the DLLS will share a single copy of the run-time library and will therefore share a single heap.

To set this in VC:

"Project" menu
"Settings..." menu item
in the dialog box that appears  "C/C++" tab
"Code generation" Category
in "Use run-time library:" select one of the DLL options.

(There are two DLLoptions there, one for a debug version, one for a release version.  Make sure you choose the setting that is right for the version you are creating)

Note that these settings need to be changed for EVERY version (debug. release etc) of the EXE and and DLLs that is shares memory with.
0
 

Expert Comment

by:gpbaldazzi
ID: 4312876
thanks nietod for your answer.
You had already told that I need to use the Dll version of the RTL or call a member function for deleting the object, but I didn't read carefully your previous answer.

I chosed to implement a Release function in the dll, because I dont want to have any trouble with future .exe that may need to use the Static RTL.

Once again, this is COM!
thanks a lot
GP
0

Featured Post

Netscaler Common Configuration How To guides

If you use NetScaler you will want to see these guides. The NetScaler How To Guides show administrators how to get NetScaler up and configured by providing instructions for common scenarios and some not so common ones.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Update (December 2011): Since this article was published, the things have changed for good for Android native developers. The Sequoyah Project (http://www.eclipse.org/sequoyah/) automates most of the tasks discussed in this article. You can even fin…
Jaspersoft Studio is a plugin for Eclipse that lets you create reports from a datasource.  In this article, we'll go over creating a report from a default template and setting up a datasource that connects to your database.
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.

803 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question