Dynamic DLL Loading

When linking your application to a regular DLL, you can do it with either static linking or dynamic linking. Static linking is easier but if a DLL does not exist, your application cannot be started. Dynamic linking is a little bit harder but offers much more flexibility. If a DLL is not found, you can show a dialog box to inform a user and disable some features in your application that use a missing DLL. Missing DLL does not prevent your application to run normally.

This class offers a very easy way to dynamically load a DLL and use it afterwards. The class as it is should not be instantiated. You should derive a class of your own.

Step 1:

Class TBaseModule is a base class for all derived classes. Each derived class corresponds to one DLL.

#define BM_OK                   0
#define BM_DLLNOTFOUND          1
#define BM_INVALIDEXPORT        2
class TBaseModule {
    protected:
        int ErrorCode;        // One of BM_xxx defines
        int FunctionErrorCode;
        CString DLLName;
        HINSTANCE DLLHandle;
    public:
        TBaseModule(CString name);
        virtual ~TBaseModule();
        virtual BOOL Create(void);
        virtual void Destroy(void);
        int GetErrorCode(void)                  { return ErrorCode; }
        int GetFunctionErrorCode(void)          { return FunctionErrorCode; }
        CString& GetDLLName(void)               { return DLLName; }
};
Step 2:

Following is an implementation of this class:

TBaseModule::TBaseModule(CString name)
{
    DLLName = name;
    DLLHandle = NULL;
    ErrorCode = BM_OK;
    FunctionErrorCode = BM_OK;
}
TBaseModule::~TBaseModule()
{
    Destroy();
}
BOOL TBaseModule::Create(void)
{
    DLLHandle = ::LoadLibrary(DLLName);
    if (DLLHandle == NULL) {
        ErrorCode = BM_DLLNOTFOUND;
        return FALSE;
    }
    return TRUE;
}
void TBaseModule::Destroy(void)
{
    if (DLLHandle) {
        ::FreeLibrary(DLLHandle);
        DLLHandle = NULL;
    }
}
Step 3:

Now, let's assume that you want to dynamically load a DLL which exports the following functions and has a name "TEST.DLL" (DLL has a standard calling convention):

void ExportedFunction1(int code, const char *str);
WORD ExportedFunction2(DWORD *data);
char *ExportedFunction3(int code);
Step 4:

You have to define a type for each exported function and then derive a class TTestModule from base class TBaseModule.
 

typedef void (__stdcall *FExportedFunction1)(int code, const char *str);
typedef WORD (__stdcall *FExportedFunction2)(DWORD data);
typedef char * (__stdcall FExportedFunction3)(int code);
class TTestModule : public TBaseModule {
private:
    FExportedFunction1 FunctionExportedFunction1;
    FExportedFunction2 FunctionExportedFunction2;
    FExportedFunction3 FunctionExportedFunction3;
public:
    TTestModule(CString dllname);
    virtual BOOL Create(void);
    virtual void Destroy(void);
    void Function1(int code, const char *str);
    WORD Function2(DWORD data);
    char *Function3(int code);
};
Step 5:

Implementation of this derived class is the following:
 

TTestModule::TTestModule(CString dllname)
            :TBaseModule(dllname)
{
    FunctionExportedFunction1 = NULL;
    FunctionExportedFunction2 = NULL;
    FunctionExportedFunction3 = NULL;
}
BOOL TTestModule::Create(void)
{
    if (TBaseModule::Create()) {
        FunctionExportedFunction1 = (FExportedFunction1)::GetProcAddress(DLLHandle,_T"ExportedFunction1");
        FunctionExportedFunction2 = (FExportedFunction2)::GetProcAddress(DLLHandle,_T"ExportedFunction2");
        FunctionExportedFunction3 = (FExportedFunction3)::GetProcAddress(DLLHandle,_T"ExportedFunction3");
        if (FunctionExportedFunction1 && FunctionExportedFunction2 && FunctionExportedFunction3)
            return TRUE;
        ErrorCode = BM_INVALID_EXPORT;
        return FALSE;
    }
    return FALSE;
}
void TTestModule::Destroy(void)
{
    TBaseModule::Destroy();
    FunctionExportedFunction1 = NULL;
    FunctionExportedFunction2 = NULL;
    FunctionExportedFunction3 = NULL;
}
void TTestModule::Function1(int code, const char *str)
{
    if (DLLHandle == NULL)
        TRACE0(_T("TEST.DLL is not loaded - Use Create()\n"));
    FunctionErrorCode = BM_OK;
    if (FunctionExportedFunction1)
        FunctionExportedFunction1(code,str);
    else
        FunctionErrorCode = BM_INVALIDEXPORT;
}
WORD TTestModule::Function2(DWORD data)
{
    if (DLLHandle == NULL)
        TRACE0(_T("TEST.DLL is not loaded - Use Create()\n"));
    FunctionErrorCode = BM_OK;
    if (FunctionExportedFunction2)
        return FunctionExportedFunction1(data);
    FunctionErrorCode = BM_INVALIDEXPORT;
    return 0;
}
char *TTestModule::Function3(int code)
{
    if (DLLHandle == NULL)
        TRACE0(_T("TEST.DLL is not loaded - Use Create()\n"));
    FunctionErrorCode = BM_OK;
    if (FunctionExportedFunction3)
        return FunctionExportedFunction3(code);
    FunctionErrorCode = BM_INVALIDEXPORT;
    return NULL;
}
Step 6:

All you have to do to use this class is to declare it (yu can do it in several different ways: (1) as a data member of your CWinApp derived class, (2) as a local object within a function, (3) as a global object and (4) as a data member of any class). You have to call Create() and check return code. If it is FALSE, you can check error code and decide to abandon using DLL (since it is not loaded) or use it (since some functions are not loaded because they do not exist within a DLL).
 

TTestModule testDll(_T("TEST.DLL"));
....
BOOL retcode = testDll.Create();
if (retcode == FALSE) {
    if (testDll.GetErrorCode() == BM_INVALIDEXPORT)
        ::AfxMessageBox(_T("TEST.DLL does not contain all functions"));
    else if (testDll.GetErrorCode() == BM_DLLNOTFOUND)
        ::AfxMessageBox(_T("TEST.DLL is not found"));
}
....
testDll.Function1(0,NULL);
if (testDll.GetFunctionErrorCode() == BM_INVALIDEXPORT)
    ::AfxMessageBox(_T("ExportedFunction1 does not exist in a TEST.DLL"));
....
Use this class for your own DLLs or for system DLLs which are not part of the operating system.

That's all there is to it!
 

Last updated: 17 May 1998.



Comments

  • Easier binding with a factory object with virtual member functions

    Posted by Legacy on 03/04/1999 12:00am

    Originally posted by: Ernst Versteeg

    If you have a lot of functions you need for each function a
    GetProcAddress. In this case it is easier to import with GetProcAddress
    only one function which returns an object with virtual member
    functions.

    If the dll is build with the same development system as the application
    you can use the virtual member functions of this returned object
    without prior binding with GetProcAddress.

    The base class of the returned object is abstract and serves as
    protocoll which describes which functions should be implemented
    by the dll.

    So the dll exports only a create function which returns an object
    derived from the abstract base class.

    Reply
  • You must have javascript enabled in order to post comments.

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: April 22, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Database professionals — whether developers or DBAs — can often save valuable time by learning to get the most from their new or existing productivity tools. Whether you're responsible for managing database projects, performing database health checks and reporting, analyzing code, or measuring software engineering metrics, it's likely you're not taking advantage of some of the lesser-known features of Toad from Dell. Attend this live …

  • Today's competitive marketplace requires the organization to frequently release and deploy applications at the pace of user demands, with reduced cost, risk, and increased quality. This book defines the basics of application release and deployment, and provides best practices for implementation with resources for a deeper dive. Inside you will find: The business and technical drivers behind automated application release and deployment. Evaluation guides for application release and deployment solutions. …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds