Click here to Skip to main content
15,896,201 members
Articles / Desktop Programming / Win32
Article

How to export complex classes in DLL

Rate me:
Please Sign up or sign in to vote.
2.44/5 (11 votes)
30 Jan 2008CPOL2 min read 42.3K   1K   24   3
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:

C++
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:

C++
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:

C++
// 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:

C++
// 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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Micro Focus APM
Bulgaria Bulgaria
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionWTF GC??? Pin
MSR 0174339727514-Feb-18 22:24
professionalMSR 0174339727514-Feb-18 22:24 
GeneralMy vote of 2 Pin
Tu, Song-gao27-Dec-12 21:04
Tu, Song-gao27-Dec-12 21:04 
GeneralRe: My vote of 2 Pin
csharpsucks15-Sep-14 9:46
csharpsucks15-Sep-14 9:46 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.