sommergyll.software(c++);
"Implementing a class in a dll to be instantiated in an exe program"
 

Introduction

Sometimes it is useful to put code that is meant to be shared and reused in a separate module, a dynamic link library (dll). This may save time and effort in the development of new code as some of the code is already implemented and tested.
Implementing a dll that contains global functions is fairly simple, but unfortunately, implementing a class inside a dll involves slightly more consideration.

A Class In Dll Example

We will implement a test dll that contains a class. Then we will see how this class can be instantiated, accessed and deleted from an exe program. Note that this is only tested on MS Windows XP.

The dll will consist of an interface, a class implementation and a factory function. First we will show the structure of the interface.

The Interface Listing

The listing below, IMyInterface.h, is useful to easily enable the exe to access the class member functions using the instantiated class object. Utilising the virtual keyword, we can automatically get a hold of the member function address from its vtable and thereby safely call the member function.

 

Note that the __stdcall keyword is necessary to get a calling signature that matches that of the exe. As this is supposed to be an interface, the member functions should be made pure virtual. This interface file should be included with the exe.

// IMyInterface.h
// Copyright Sommergyll Software (c) 2007-2010
//
#pragma once

// Interface that declares 
// the virtual member functions
class IMyInterface
{
public:
    IMyInterface(void){}
    virtual ~IMyInterface(void){}

    // Need to use __stdcall on functions
    // that should be called from within the exe.
    virtual bool __stdcall SetNumber(int aNumber) = 0;
    virtual bool __stdcall AddNumber(int aNumber) = 0;
    virtual bool __stdcall GetNumber(int& aNumber) const = 0;
};

The Class Implementation File

Here, in MyClass.h, we have put both the class declaration and the definition in the same file, for simplicity. In a real implementation, the class definition should obviously be located in a .cpp file.
We inherit from the simple test interface and also add one member variable to store the value in. The definition is equally simple but will have enough code to show how the dll works. Note that a member function does not need to return a boolean.

// MyClass.h
// Copyright Sommergyll Software (c) 2007-2010
//
#include "IMyInterface.h"

// Concrete class implementing the
// interface member functions.
class MyClass : public IMyInterface
{
public:
    MyClass();
    ~MyClass();

public: // From IMyInterface
    bool __stdcall SetNumber(int aNumber);
    bool __stdcall AddNumber(int aNumber);
    bool __stdcall GetNumber(int& aNumber) const;

private:
    int iNumber;
};


//
// Implementation
//

MyClass::MyClass()
: iNumber(0) 
{
}

MyClass::~MyClass()
{
}

bool __stdcall MyClass::SetNumber(int aNumber)
{
    iNumber = aNumber;
    return true;
}

bool __stdcall MyClass::AddNumber(int aNumber)
{
    iNumber += aNumber;
    return true;
}

bool __stdcall MyClass::GetNumber(int& aOutNumber) const
{
    aOutNumber = iNumber;
    return true;
}

The Dll File

Below, in DllTest.cpp, is the dll factory function. It is a simple and convenient way to instantiate an object of our test class, as we cannot (easily) call new on the class in the exe since we don't have the address to the constructor. Using a factory function is an established way of handling the object creation.
The extern "C" __declspec(dllexport) before the function is necessary to expose the function so that the exe can access it using the GetProcAddress, as we will show next.

// DllTest.cpp
// Copyright Sommergyll Software (c) 2007-2010
//
#include "IMyInterface.h"
#include "MyClass.h"

// Creates and returns an object
// of IMyInterface. Caller takes 
// ownership of instance.
extern "C"
__declspec(dllexport) IMyInterface* DllGetObject()
{
    IMyInterface* pObj = static_cast<IMyInterface*>(new MyClass);
    return pObj;
}

The Exe Source Code

Looking at DllRunTest.cpp, we declare the pointer to the class factory function using typedef. In the main function, we first load the dll into the address space of the exe, with LoadLibrary. Then we need to get a pointer to the factory function by calling GetProcAddress with the correct function name. Using the function pointer, we are now ready to actually instantiate the class and assigning it to the interface pointer. Using the interface (base class) is not mandatory, but convenient.

Thanks to making the class member functions virtual, they are now available for being unit tested. If everything works as expected, the "successful" message will be printed on the screen.

When finished with the instantiated class and the dll, make sure to tidy up the resources.

// DllRunTest.cpp 
// Copyright Sommergyll Software (c) 2007-2010
// 

#include <windows.h>
#include <tchar.h>
#include <iostream>
#include "IMyInterface.h"

// typedef for the factory function
typedef IMyInterface* (__stdcall *DLLGETOBJECT)(void);

int _tmain(int argc, _TCHAR* argv[])
{
    // Load the dll into the address space
    HINSTANCE hInstDll = LoadLibraryA("DllTest");
    if (hInstDll == NULL)
        return 0;      
    
    // Retrieve a pointer to the factory function
    DLLGETOBJECT pDllGetObject = 
        (DLLGETOBJECT) GetProcAddress(hInstDll, "DllGetObject");        
    // Create the object using the factory function
    IMyInterface* pMyObject = (pDllGetObject)();
    if (pMyObject == NULL)
        return 0;
    
    // Check the Dll API by calling
    // member functions of the created object
    int resultNumber = 0;
    bool result = pMyObject->SetNumber(12);
    result = result && pMyObject->AddNumber(27);   
    result = result && pMyObject->GetNumber(resultNumber);

    // Simple unit test
    if (result && resultNumber == 39)
    {
        std::cout << "Successfully tested dll class members.";
    }
    else   
    {
        std::cout << "Dll test failed!";
    }

    // Clean up resources
    delete pMyObject;
    FreeLibrary(hInstDll);   

	return 0;
}

 

 

Front Page | Disclaimer

© Copyright 2003-2010 Sommergyll Software. All Rights Reserved.

Dynamic dll module - instantiating a class object