Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

How to export complex classes in DLL

0.00/5 (No votes)
30 Jan 2008 1  
Export derived classes, design patterns, etc.

Introduction

Since I spent the last few days in searching how to export a class from DLL and found a lot of stuff on DLLs but merely anything that helped me, I decided to summarize the useful information in this brief tutorial.

Background

To most of us involved in OO programming, it's rather natural to expect to be able to export whole objects from a DLL. The good news is it's doable. The bad news: it's not that obvious. And, to make things even worse: try to export inherited classes or functions that return class pointers.

Solution

It's not the only way to solve this, and I don't know whether it's the best solution. It's rather an approach that provides flexibility and robustness.

The problems with exporting classes from a DLL are the mangled names of functions due to the C++ style. With functions, it's quite easy - just define them as extern "C", and you've got it. Since extern "C" has no effect on classes, you have to come up with something else. One way is to derive your class from a DLL wrapper class with pure virtual member functions for every function you want to export with the class. The thing your class really needs to inherit is the virtual table of the functions of the wrapper class. The wrapper class may look like an interface class, and there are cases when you can use it as one, but you must think of it rather as exporting a wrapper that is meant for only one real class. This is quite obvious in a simple inheritance scheme where you have a real base class and a real derived class.

classical_inheritence_scheme.JPG

When you want to have the same structure in a DLL and export Derived1 or Derived2, you must reform your scheme to:

dll_inheritence_scheme.JPG

In C++, that would look like:

class DllWrapperBase  
{
public:
    DllWrapperBase();
    virtual ~DllWrapperBase();

    virtual long __stdcall        test1()=0;
    virtual float __stdcall        test2()=0;
};          

 class Base : public DllWrapperBase  
{
public:
    Base();
    virtual ~Base();

    virtual long __stdcall        test1(){return 1;}
    virtual float __stdcall        test2(){return 1.8f;};
}; 
class DllWrapperDerived : public Base  
{
public:
    DllWrapperDerived();
    virtual ~DllWrapperDerived();

    virtual long __stdcall    test3()=0;
    virtual float __stdcall    test4()=0;
};
class Derived : public DllWrapperDerived  
{
public:
    Derived();
    virtual ~Derived();
    virtual long __stdcall    test3(){return 3;}
    virtual float __stdcall    test4(){return 3.9f;}
};

To make it more complicated, we introduce a Factory class that produces objects of the derived class. It's a common design pattern, and would look as this:

enum eProducts
{
    PRODUCT1
};

class Base;
class Derived;

class DllWrapperFactory  
{
public:
    DllWrapperFactory();
    virtual ~DllWrapperFactory();
    virtual Base * __stdcall    CreateProduct(eProducts iValue)=0;
protected:
    virtual Derived * __stdcall CreateDerived()=0;
};

class Factory : public DllWrapperFactory  
{
public:
    Factory();
    virtual ~Factory();
    Base * __stdcall    CreateProduct(eProducts iValue);        
protected:
    Derived * __stdcall CreateDerived();
};

Then, we add the main DLL files:

// dll_export.h
#include "Factory.h"

extern "C" __declspec(dllexport) DllWrapperFactory * returnFactory()
{
    DllWrapperFactory * pObj = 
      static_cast<dllwrapperfactory*>(&FactorySingleton::Instance());
    return pObj;
}
// dll_export.cpp
#include "stdafx.h"
#include "dll_export.h"


BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
        case DLL_PROCESS_ATTACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
    }
    return TRUE;
}

You use the newly created DLL like this:

// dll_tester.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <stdlib.h>
#include <windows.h>
#include "Factory.h"
#include "Derived.h"

typedef Factory * (__stdcall *DLLGETFACTORY)(void);

int main(int argc, char* argv[])
{
    printf("Hello World!\n");

    HINSTANCE hInstDll = LoadLibrary("dll_test.dll");
    if(!hInstDll) printf("Failed to load dll\n");

    DLLGETFACTORY pDllGetFactory = 
      (DLLGETFACTORY) GetProcAddress(hInstDll, "returnFactory");
    // Create the object using the factory function
    Factory * pMyFactory = (pDllGetFactory)();
    if (pMyFactory == NULL)
        return 0;
    Derived * d = (Derived *)pMyFactory->CreateProduct(PRODUCT1);
    
    int i = d->test1();
    float f = d->test2();
    int j = d->test3();
    float e = d->test4();

    system("PAUSE");

    return 0;
}

You have to keep in mind that in order to use any class through a DLL, you have to include its header in the application.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here